SQL if query functionality - mysql

I want to write a query to determine the success rate for every day for each different mode. I did write a query that'll just group by date and mode which serves my purpose but one of my seniors wrote this following query which also works but I am just unable to understand how the if clause is working. I'll add a bit of the query here -
SELECT
dt,
sum(if(mode='A',success,0))AS a_s,
sum(if(mode='A',total,0))AS a_t,
sum(if(mode='B',success,0))AS b_s,
sum(if(mode='B',total,0))AS b_t,
sum(if(mode='C',success,0))AS c_s,
sum(if(mode='C',total,0))AS c_t,
sum(if(mode='D',success,0))AS d_s,
sum(if(mode='D',total,0))AS d_t,
sum(if(mode NOT in('A','B','C','D'),success,0))AS other_s,
sum(if(mode NOT in('A','B','C','D'),total,0))AS other_t
FROM
(SELECT
mode,
date(addedon)AS dt,
sum(if(status in('success','partial'),1,0))AS success,
count(*)AS total
FROM `a_huge_ass_table`
WHERE `studentid`=159633 AND addedon>'2021-01-15'
GROUP BY mode,date(addedon)
)AS t
Here I am unable to understand how sum(if(mode='A',success,0))AS a_s, - this if clause is working. If the condition is true then the clause is returning success? how does that work does adding success also somehow verify that its status is a success case? I cant find this on google.

First, if() is not standard SQL. I recommend rewrite this using case:
sum(case when mode = 'A' then success else 0 end) as a_s,
sum(case when mode = 'A' then total else 0 end) as a_t,
and so on.
Second, this query is missing the final group by dt. Otherwise it produces one row, rather than a separate row for each dt value.
This is called conditional aggregation. Every row in the final result set represents a group of rows from the subquery. Within this group, some have mode = 'A' and some do not. For the ones with mode = 'A' the above sums the value of success and total.
There is no need for a subquery by the way. That just slows down the query. I would recommend writing the query as:
SELECT date(addedon) as dt
SUM( mode = 'A' AND status IN ('success', 'partial') ) as a_success,
SUM( mode = 'A' ) as a_total,
. . .
FROM `a_huge_ass_table`
WHERE studentid = 159633 AND addedon >= '2021-01-15'
GROUP BY date(addedon);
Note that this uses a MySQL extension where boolean expressions are treated as integers, with "1" for true and "0" for false.

Related

Using two sets of conditions in where clause

I'm trying to get the count of items in my table where it should satisfy these conditions
status = active
type = Pre-order
date = $date_input
OR
status = active
type = Both
date = $date_input
I'm trying to use this statement but I'm pretty sure it's messed up.
SELECT COUNT(id) as count_date from date_restriction
where (date='$date_input' AND status='active' AND type='Pre-order')
OR (date='$date_input' AND status='active' AND type='Both')
I also tried this to no avail
SELECT COUNT(id) as count_date from date_restriction
where date='$date_input' AND status='active' AND type='Pre-order' OR type='Both'
Whe you have mixed AND and OR condition you need () for the or clause
SELECT COUNT(id) as count_date
from date_restriction
where date='$date_input'
AND status='active'
AND ( type='Pre-order' OR type='Both')
or instear of several or condition you could use a IN clause
AND type IN ('Pre-order', 'Both')
anyway you should avoid the use of php var in SQL you are at risk for sqlinjection .. for avoid this you should take a look at prepared statement and binding param for your db driver
Your code should work. I would write this as:
select count(*) as count_date
from date_restriction
where date = ? AND status = 'active' AND
type in ('Pre-order', 'Both');
Note: The ? is for a parameter so you are not munging the query string with input values.
If I had to guess why this isn't working, I would speculate that one or both of the dates have a time component. You might try:
where date(date) = date(?) . . .
to be sure you are matching on the date, regardless of time.
SELECT count(id) as count_date
FROM date_restriction
WHERE date='$date_input' AND status='active' AND (type='Pre-order' OR type='Both');
Here, date and status both fields are common in your case; hence, don't wrap it in parenthesis whereas, add OR condition for type field only and con-cat it with AND in WHERE clause.

SQL: Something wrong with inheriting variables for NULL next-row values

I'm trying to inherit value from previous row (based on correct subscription_id + checking for IS NULL subscription_status), but something goes wrong and I get incorrect value.
Take a look at screenshot.
If I'm not mistaken it also called last non-null puzzle, but examples of possible solution for other DB provide window function with IGNORE NULLS.
But, I'm using MySQL 8.x and it doesn't support this function.
I'm sorry, but SQL fiddle doesn't provide correct text-value for variables in my code :(
https://www.db-fiddle.com/f/wHanqoSCHKJHus5u6BU4DB/4
Or, you can see mistakes here:
SET #history_subscription_status = NULL;
SET #history_subscription_id = 0;
SELECT
c.date,
c.user_id,
c.subscription_id,
sd.subscription_status,
(#history_subscription_id := c.subscription_id) as 'historical_sub_id',
(#history_subscription_status := CASE
WHEN #history_subscription_id = c.subscription_id AND sd.subscription_status IS NULL
THEN #history_subscription_status
ELSE
sd.subscription_status
END
) as 'historical'
FROM
calendar c
LEFT JOIN
subscription_data sd ON sd.date = c.date AND sd.user_id = c.user_id AND sd.subscription_id = c.subscription_id
ORDER BY
c.user_id,
c.subscription_id,
c.date
I expect to get results for this query in this way:
IMPORTANT: I'm going to use this code for a lot of data (about 1 mln rows), so it very important for me to avoid additional select or subquery that can slow down the execution of the query.

Odd behavior with user defined variables in MySQL

I am building a report and using user defined variables to keep the query as compact as possible. What I am noticing in MySQL Workbench and in PHP is that they don't always work properly.
For example:
SELECT
#NewSales := SUM(CASE WHEN `v`.`new_used`='N' THEN 1 ELSE 0 END) AS `NewSales`,
#UsedSales := SUM(CASE WHEN `v`.`new_used`='U' THEN 1 ELSE 0 END) AS `UsedSales`,
#UsedSales + #NewSales AS `TotalSales`
FROM `fi_sales` `s`
LEFT JOIN `vehicles` `v` ON `v`.`stock_number`=`s`.`stock_number`
If I run the above query in Workbench, the first run outputs TotalSales=NULL:
NewSales, UsedSales, TotalSales
3418, 2889, NULL
If I refresh the query, the output produces expected result for TotalSales:
NewSales, UsedSales, TotalSales
3418, 2889, 6307.000000000000000000000000000000
A bit strange; almost as if the variable is not usable in the same query that sets it. I usually work around it by reproducing the calculation without using variables.
My next problem is that if I copy the same query from Workbench into my application (PHP) the TotalSales output will produce "0" zero.
I am sure there is a perfectly good explanation what is going on here but I am having trouble finding it. Any answers are greatly appreciated.
You're in non-deterministic territory using user-defined variables in a query that changes them, and the explanation is straightforward: the answer you get is actually from the previous run of the same query.
UDVs are scoped to your individual database connection. Their values persist between queries, but not across connections. This query is giving you the value of #UsedSales + #NewSales from before the query is run, not after. (Why? Because it just is. There is not a reason... it could go either way. See below.)
SET #UsedSales = 1, #NewSales = 2; and run your query again. Total will be 3 in the next run, clearly the wrong answer (compared to what you expected) but not wrong in the sense that the server is free to resolve these in any order it likes, because they look like constants.
As a general rule, other than in SET statements, you should never assign a value to a user variable and read the value within the same statement.
...
For other statements, such as SELECT, you might get the results you expect, but this is not guaranteed.
...
The order of evaluation for expressions involving user variables is undefined.
https://dev.mysql.com/doc/refman/5.7/en/user-variables.html
You're trying to solve a problem that isn't really a problem.
Instead, either do this:
SELECT
SUM(CASE WHEN `v`.`new_used`='N' THEN 1 ELSE 0 END) AS `NewSales`,
SUM(CASE WHEN `v`.`new_used`='U' THEN 1 ELSE 0 END) AS `UsedSales`,
SUM(CASE WHEN `v`.`new_used` IN ('N','U') THEN 1 ELSE 0 END) AS `TotalSales`
FROM `fi_sales` `s`
LEFT JOIN `vehicles` `v` ON `v`.`stock_number`=`s`.`stock_number`;
Or if you insist, make a derived table (here, named x) and do the extra addition of the resulting columns.
SELECT
x.NewSales,
x.UsedSales,
x.NewSales + x.UsedSales AS TotalSales
FROM (
SELECT
SUM(CASE WHEN `v`.`new_used`='N' THEN 1 ELSE 0 END) AS `NewSales`,
SUM(CASE WHEN `v`.`new_used`='U' THEN 1 ELSE 0 END) AS `UsedSales`
FROM `fi_sales` `s`
LEFT JOIN `vehicles` `v` ON `v`.`stock_number`=`s`.`stock_number`
) x;
This materializes the inner result into a temporary table that is discarded as soon as the query finishes executing.
Or, if you really want clever and short, go with this:
SELECT
COUNT(`v`.`new_used`='N' OR NULL) AS `NewSales`,
COUNT(`v`.`new_used`='U' OR NULL) AS `UsedSales`,
COUNT(`v`.`new_used` IN ('N','U') OR NULL) AS `TotalSales`
FROM `fi_sales` `s`
LEFT JOIN `vehicles` `v` ON `v`.`stock_number`=`s`.`stock_number`;
This works, because COUNT() counts only rows with non-null arguments, and any expression expr OR NULL coerces expr to be evaluated as a boolean expression, and thus is logically equivalent to CASE WHEN expr [IS TRUE] THEN 1 ELSE NULL END and thus can only evaluate to 1 (if expr is truthy)... or NULL (if expr is either false or null)... which an exact match for how COUNT() works.

My sql Select IF returns the else result even if the condition is true

This request returns Else result
"ELSE RESULT" even if the condition is true.
SELECT
IF(Print_Server_Type='BW',SUM(Total_Impressions_Printed),'ELSE RESULT') AS BWTotalImpressions
FROM printedjob
WHERE
Account=''
And Job_Submission_Date>="2015-08-24"
AND Job_Submission_Date<="2015-08-28";
Same request with the where condition that works fine
SELECT
SUM(Total_Impressions_Printed) AS BWTotalImpressions
FROM printedjob
WHERE
Account=''
And Print_Server_Type='BW'
And Job_Submission_Date>="2015-08-24"
AND Job_Submission_Date<="2015-08-28";
I'm using the first request because I need the result to be a column from a select.
You are mixing aggregate terms (like SUM(Total_Impressions_Printed)) and single-row terms (like Print_Server_Type). Seems like you're trying to return a row per value of Print_Server_Type, which means you're missing a group by clause:
SELECT Print_Server_Type,
IF(Print_Server_Type = 'BW',
SUM(Total_Impressions_Printed),
'ELSE RESULT') AS BWTotalImpressions
FROM printedjob
WHERE Account = '' AND
Job_Submission_Date >= "2015-08-24" AND
Job_Submission_Date <= "2015-08-28"
GROUP BY Print_Server_Type
I think that your ELSE statement is written incorrectly. The else shouldn't be into quotes 'ELSE RESULT'... I would start by verifying to correct format to write the query.
Fixed the issue , here is the request:
Select Account,SUM(Total_Impressions),SUM(BWTotalImpressions)
FROM (
SELECT Account,Print_Server_Type,SUM(Total_Impressions_Printed) As Total_Impressions,
IF(Print_Server_Type='Color',SUM(Total_Color_Pages_Printed),'') AS ColorTotalImpressions,
IF(Print_Server_Type='BW',SUM(Total_Impressions_Printed),
IF(Print_Server_Type='Color',SUM(Total_Black_Only_Pages_Printed),'')) AS BWTotalImpressions
FROM printedjob
WHERE Job_Submission_Date>="2015-08-24" AND Job_Submission_Date<="2015-08-28"
GROUP BY Account,Print_Server_Type
)AS T
GROUP by Account;

replace count (CASE WHEN) in this query

i m want to replace CASE WHEN in this query
SELECT label ,date,count(id) AS nb, count(CASE WHEN label='test' THEN 1 END) AS nb_test
FROM
sil_test,sil_data
WHERE
sil_test.id_t=sil_data.id_t
GROUP BY
label, date
any idea??
Here is another version of this query that works with MySQL:
SELECT label ,date,count(id) AS nb, sum(label = 'test') AS nb_test
FROM sil_test join
sil_data
on sil_test.id_t = sil_data.id_t
GROUP BY label, date;
I'm not sure what problem this solves, except for replacing ANSI standard syntax with MySQL-specific functionality. And, of course, fixing the joins so they are explicit (much, much, much recommended).