I'm currently doing this via two separate queries from PHP, but would love to optimize and somehow in a single query.
First query..
SELECT `referrer`
FROM `tbl_traffic_log`
WHERE `domain` = 'mysite.com'
AND `referrer` != '$referringDomain'
AND CASE WHEN `clicks_in_unique`=0 THEN 2 ELSE `clicks_out_unique`/`clicks_in_unique` END < 1.4
ORDER BY RAND()
LIMIT 1
..and if mysql_num_rows shows no results, I do a second query to try again and check if there are any results minus the referrer != 'partner1.com' part.
The code is basically trying to find a random trade partner who ISN'T the partner who sent that click, but if there are no matches, as a last resort it's ok to send back, provided it matches the other criteria.
I'm pretty sure there is a way to do this, but just can't find a way after searching (probably because I'm not understanding the problem enough to type in the right thing).
Any other critique of the query is welcome as well.
Thank you :)
I think you can do this like this:
SELECT `referrer`
FROM `tbl_traffic_log`
WHERE `domain` = 'mysite.com'
AND CASE WHEN `clicks_in_unique`=0 THEN 2 ELSE `clicks_out_unique`/`clicks_in_unique` END < 1.4
ORDER BY `referrer` != '$referringDomain' desc, RAND()
LIMIT 1
The idea is to put the condition in the order by. The condition (in MySQL) evaluates to either 0 or 1, so we want where the condition is true first (hence the desc). It then chooses a random row. If there are no rows where the condition is true, then it chooses a random row.
Related
Say I have a table with the structure
recordNumber: INTEGER (autoincrement)
insertedOn: DATETIME
Normally data gets inserted into the table it increments the recordNumber and insertedOn is always current time. Normally the following should be true
select insertedOn order by recordNumber === select insertedOn order by insertedOn
But that's actually not the case the question I have is how do I query the database so I can find the first recordNumber that would break the condition.
You can use LAG window function, but it depends on the particular database you are using.
If we assume that your increments are by '1', then this is a little more generic:
select top 1 *
from YrTbl Ths
where exists (select 1
from YrTbl Prev
where Prev.recordNumber+1=Ths.recordNumber
and Prev.insertedOn>=Ths.insertedOn
)
order by Ths.RecordNumber
TOP n might work a little differently in your environment; if you are using MySQL you might like to use LIMIT 1 at the end of the query, for example.
Hopefully this is a simple one but I am most likely over complicating this for myself. Purpose of this code is to find previous operation name within a specified list of operations that is still open and returning it. If it's closed to say 'CLOSED'.
So far I'm using subquery to get the correct operation name, as expected I'm getting some null results which indicates to me that operation is closed. I wanted to wrap my subquery into a case statement to say something along the lines of CASE WHEN it's null then 'CLOSED' else operation_name end.
(select work_center_no
from shop_order_operation
where order_no = so.order_no
and release_no = so.release_no
and sequence_no = so.sequence_no
and work_center_no in ('CNC','EXPF','LS3M','LS4M','LS6M','PLAS','SAW','TBPL','EXSAW')
and oper_status_code in ('Released','In Process') order by operation_no fetch first 1 row only )
Above is my subquery. Hope this makes sense.
Thanks!
K
You can nicely default a value using IFNULL, just change your select clause to this:
select IFNULL(work_center_no, 'CLOSED')
So far following is my scenario :
Parameters controlled by user: (These parameters are controlled by a dashboard but for testing purposes I have created sql parameters in order to change their values)
SET #device_param := "all devices";
SET #date_param_start_bar_chart := '2016-09-01';
SET #date_param_end_bar_chart := '2016-09-19';
SET #country_param := "US";
SET #channel_param := "all channels";
Query that runs at the back-end
SELECT
country_code,
channel_report_tag,
SUM(count_more_then_30_min_play) AS '>30 minutes',
SUM(count_15_30_min_play) AS '15-30 Minutes',
SUM(count_0_15_min_play) AS '0-15 Minutes'
FROM
channel_play_times_cleaned
WHERE IFNULL(country_code, '') =
CASE
WHEN #country_param = "all countries"
THEN IFNULL(country_code, '')
ELSE #country_param
END
AND IFNULL(channel_report_tag, '') =
CASE
WHEN #channel_param = "all channels"
THEN IFNULL(channel_report_tag, '')
ELSE #channel_param
END
AND iFnull(device_report_tag, '') =
CASE
WHEN #device_param = "all devices"
THEN iFnull(device_report_tag, '')
ELSE #device_param
END
AND playing_date BETWEEN #date_param_start_bar_chart
AND #date_param_end_bar_chart
GROUP BY channel_report_tag
ORDER BY SUM(count_more_then_30_min_play) DESC
limit 10 ;
The index that I have applied is
CREATE INDEX my_index
ON channel_play_times_cleaned (
country_code,
channel_report_tag,
device_report_tag,
playing_date,
channel_report_tag
)
I have followed this link : My SQL Index Cook-Book Guide to create my index.
However the EXPLAIN keyword while executing the above query tells me that there is no index used.
I want to what am I doing wrong over here ?
You use functions and case expression in the first 3 where condition. Simple field index cannot be used to speed up such look ups.
MySQL could potentially use an index for the playing_date criteria, but that field is not the leftmost in the cited index, therefore the cited index is not suitable for that either.
If I were you, I would remove the logic from the where criteria and moved that into the application layer by constructing such an sql statement that has the case conditions resolved and emits only the necessary sql.
Your CASE expressions in the WHERE clause are forcing full table scans. Clearly, they have to go... but how?
You have to think like the optimizer and remember that its job is to avoid as much work as possible.
Consider this query:
SELECT * FROM users
WHERE first_name LIKE '%a%';
Every row must be read to find all first_name values containing the letter 'a'. Very slow.
Now, this one:
SELECT * FROM users
WHERE first_name LIKE '%a%'
AND 2 < 1;
For each row, you're asking the server to check the first_name again and to include only rows where 2 is a smaller number than 1.
Is it slow, or fast?
It's very fast, because the optimizer detects an Impossible WHERE. There is no point in scanning the rows because 2 < 1 is always false.
Now, use this logic to tell the optimizer what you really want:
Not this:
WHERE IFNULL(country_code, '') =
CASE
WHEN #country_param = "all countries"
THEN IFNULL(country_code, '')
ELSE #country_param
END
AND
But this:
WHERE
(
(
#country_param = "all countries"
)
OR
(
#country_param != "all countries"
AND
country_code = #country_param
)
)
AND ...
The difference should be stark. If #country_param = "all countries" the second test is not needed, and otherwise, only the rows with the matching country are needed and this portion of the WHERE clause is false by definition for all other rows, allowing an index on country_param to be used.
One or the other of these OR'ed expressions is always false, and that one will be optimized away, early -- never evaluated for each row. The expression #country_param != "all countries" should be treated no differently than the expression 2 < 1 or 2 > 1. It is not going to change its truthiness based on the data in the rows, so it only needs to be evaluated once, at the beginning.
Repeat for the other CASE. You should almost never pass columns as arguments to functions in the WHERE clause because the optimizer can't "look backwards through" functions and form an intelligent query plan.
The other answers have explained why your query is slow. I will explain what you should do.
Write code to "construct" the query. It would either leave out the test for country_code if the user said "all countries", or it add in AND country_code = "US". No #variables, no CASE, etc.
Then, one 5-column index won't work except for a few cases. Instead, get a feel for what users are asking for, then build a few 2-column indexes to cover the popular cases.
I need to be able to display the results of the below query in a specific order. For example: showing featured listings before the rest of the results.
WHERE IS `featured-listing` && WHERE IS NOT `featured-listing`
Could probably run 2 queries and a union right, bu is that the most effective solution? I know this can be done with one query I just cant remember how it/s done. Any and all help is appreciated.
SELECT `Assigned-Regions`,`Description`,`Category`,`Start-Date` FROM `adds` WHERE `Status` = "Active" ORDER BY `Start-Date` DESC
I would use a case statement for ORDER BY.
So something like
SELECT ... ORDER BY (CASE WHEN featured-listing THEN 1 ELSE 2) ASC, some-other-field ASC
Sounds like all you need is to add an ORDER BY clause to your query.
If featured-listing column is integer datatype and contains values of 1 or 0 (1=is featured listing, 0=not a featured listing), then you could simply add something as simple as:
ORDER BY `featured-listing` DESC, `Start-Date` DESC
Or, you could use an expression:
ORDER BY IF(`featured-listing`=1,1,0) DESC, `Start-Date` DESC
you can do conditional ordering.. not sure what featured-listing is without seeing some data but this is the logic for conditional ordering
SELECT `Assigned-Regions`,`Description`,`Category`,`Start-Date`
FROM `adds`
WHERE `Status` = "Active"
ORDER BY
CASE WHEN `featured-listing` THEN 1 ELSE 2 END ASC,
`Start-Date` DESC
We have a table that has either NULL or "Accepted" as values. My query returns about 250 rows.
If I add a where condition of -
AND Description = 'Accepted'
my 250 rows return in 2 seconds.
However, if I add a where condition of -
ISNULL(Description, '') = 'Accepted'
my 250 rows return in 47 seconds.
Has anyone encountered performance issues with using the ISNULL function? Unfortunately I am programatically limited to having to use ISNULL at this point.
When you include a field inside of a function, it changes how the optimizer has to run and forces it to ignore indexes.
see here: What makes a SQL statement sargable?
You can also bypass the functions entirely by using:
WHERE (Description = 'Accepted' OR Description IS NULL)
Using
ISNULL(Description, '') = 'Accepted'
in your where condition doesnt make any sense in this case. If the description is null, the original where clause of
AND Description = 'Accepted'
will still suffice.
You are basically comparing '' with 'Accepted' in every row where the description is null.
Please elaborate on what you are trying to accomplish with the query, i think you might be going in the wrong direction.
If you are trying to use this in the WHERE condition, use IS NULL, not ISNULL
SELECT field FROM table WHERE description is null
or the opposite
SELECT field FROM table WHERE NOT description is null