select case when not working as expected - mysql

I'm trying to understand how this works but can't figure it out yet.
I have made this simple uery to test the case-when-then-end clause...
SELECT case when quantity > 3
then count(*) end the_count_a,
case when quantity <= 3
then count(*) end the_count_b
FROM STOCK
my stock table has 30 items with different quantities, only 10 items have quantity over 3 but this is always returning 30.... WHY?
I think it should be returning two columns with values: 10 and 20
Any help will be appreciated!
Thx,
Leo

The value of count(*) means the count of all records (in the current group), regardless of where it is placed. If you want to count records that match a condition, you need to invert your case statement:
select count(case when quantity > 3 then 1 end) the_count_a,
count(case when quantity <= 3 then 1 end) the_count_b
from stock

SELECT
count(case when quantity > 3 then 1 else null end) end the_count_a,
count(case when quantity <= 3 then 1 else null end) end the_count_b
FROM STOCK

The aggregate function COUNT() in absense of a GROUP BY will return all rows in the table which have not been filtered by a WHERE clause. In your case, what you actually need are two subselects or a UNION, depending if you want columns or rows back:
/* Return columns with subselects */
SELECT
(SELECT COUNT(*) FROM STOCK WHERE quantity > 3) AS the_count_a
(SELECT COUNT(*) FROM STOCK WHERE quantity <= 3) AS the_count_b
MySQL is lenient about the presence of a FROM clause, so it can be omitted from the outer query.
/* Return rows instead of columns with UNION */
SELECT
COUNT(*) AS the_count,
'the_count_a'
FROM STOCK WHERE quantity > 3
UNION ALL
SELECT
COUNT(*) AS the_count,
'the_count_b'
FROM STOCK WHERE quantity <= 30

Related

Mysql - most efficient way to retrieve data based on multiple selects and wheres

I'm having trouble finding the most efficient way of retrieving various different sumed values from a Mysql table.
Let's say I've got 4 columns - userid, amount, paid, referral.
I'd like to retrieve the following based on a user id:
1 - the sum of amount that is paid (marked as 1)
2 - the sum of amount that is unpaid (marked as 0)
3 - the sum of amount that is paid and referral (marked as 1 on both paid and referral columns)
4 - the sum of amount that unpaid and referral (marked as 0 on paid and 1 on referral columns)
I've tried an embedded select statement like this:
SELECT (
SELECT sum(payout)
FROM table1
WHERE ispaid = 0 and userid = '100'
) AS unpaid
(
SELECT sum(payout)
FROM table1
WHERE ispaid = 1 and userid = '100'
) AS paid,
(
SELECT sum(payout)
FROM table1
WHERE ispaid = 0 and isreferral = 1 and userid = '100'
) AS refpending,
(
SELECT sum(payout)
FROM table1
WHERE ispaid = 1 and isreferral = 1 and userid = '100'
) AS refpaid
This works, but its slow (or at least feels like it could be quicker) on my server, around 1.5 seconds.
I'm sure there is a better way of doing this with a group statement but can't get my head around it!
Any help is much appreciated.
Thanks
You can use conditional expressions inside SUM():
SELECT
SUM(CASE WHEN ispaid=0 THEN payout END) AS unpaid,
SUM(CASE WHEN ispaid=1 THEN payout END) AS paid,
SUM(CASE WHEN ispaid=0 AND isreferral=1 THEN payout END) AS refpending,
SUM(CASE WHEN ispaid=0 AND isreferral=1 THEN payout END) AS refpaid
FROM table1
WHERE userid = '100'
If a given row is not matched by any CASE...WHEN clause, then the value of the expression is NULL, and SUM() ignores NULLs. You could also have an ELSE 0 clause in there if you want to be more explicit, since SUM() will not be increased by a 0.
Also make sure you have an index on userid in this table to select only the rows you need.

How to calculate quantity on hand for every lot (mysql)

This is the transaction details table
I am trying to design a Mysql inventory database.
I consider every type: 1 row a product lot (batch).
The type column has 1 for IN and 0 for OUT for each transaction.
detail_id is referencing the id column.
How can I get this result:
id item sum(quantity)
1 1 3 [10-(5+2)]
4 1 0 (5-5)
6 2 20 20
WITH cte AS (
SELECT *,
SUM(detail_id IS NULL) OVER (PARTITION BY item ORDER BY id) group_num
FROM details
)
SELECT MIN(id) id,
item,
SUM( CASE type WHEN 1 THEN quantity
WHEN 0 THEN -quantity
END ) `sum(quantity)`
FROM cte
GROUP BY item, group_num;
fiddle
You can use this:
SELECT
lots.id,
MIN(lots.item) AS item,
MIN(lots.quantity) - IFNULL(SUM(details.quantity), 0) AS quantity
FROM (
SELECT id, item, quantity
FROM details
WHERE type = 1
) lots LEFT JOIN details ON lots.id = details.detail_id
GROUP BY lots.id
ORDER BY lots.id
demo on dbfiddle.uk
I have modified your fiddle. I did change one thing to your existing table - you need to specify detail_id, wherever it is null, so that we can group the result on that.
Final query will look like
select detail_id, item,
(in_sum - out_sum) as `sum(quantity)` from
(SELECT
detail_id,
item,
sum(case when type=1 then quantity else 0 end) as in_sum,
sum(case when type=0 then quantity else 0 end) as out_sum
FROM details
group by detail_id, item) tab
Firstly getting the sum of quantity for specified type by grouping over detail_id, item and then use that result to compute the final output.

want to get all dates but when there is one or more TDBUY = 1 then this date#TDBUY=0 not

I have a table with a date column and a column named TDBUY (which can be 0 or 1). Now I do following:
SELECT tradedate,aktienstat.TDBuyPerfection,count(*) as cc from
aktienstat group by TradeDate,TDBuyPerfection HAVING cc >= '0' ORDER
BY TradeDate desc limit 100;
And get:
I don`t want to display f.e. the 2018-02-08 or 2018-02-07 with 0 count (line 1 and line 3) because there are 1 count with 1 each. But if no TDBUY then the date should be displayed with 0 count.
Can anyone here tell me please how to do it?
THANKS
Edit: It works also fine with SUM instead of Count.
SELECT tradedate,aktienstat.TDBuyPerfection,sum(aktienstat.TDBuyPerfection) as summe from
aktienstat group by TradeDate ORDER
BY TradeDate desc limit 100;ยด
We can try doing this via a pivot query:
SELECT
tradedate,
CASE WHEN cnt_1 = 0 THEN 0 ELSE 1 END AS TDBuyPerfection,
CASE WHEN cnt_1 = 0 THEN cnt_0 ELSE cnt_1 END AS cnt
FROM
(
SELECT
tradedate,
COUNT(CASE WHEN TDBuyPerfection = 0 THEN 1 END) AS cnt_0,
COUNT(CASE WHEN TDBuyPerfection = 1 THEN 1 END) AS cnt_1
FROM aktienstat
GROUP BY tradedate
) t;
The pivot trick works here because it brings the zero and one counts into a single record. There, it is relatively easy to test both counts. In your current form, it is much harder to check the counts.
The demo below shows that zero buy dates appear with only the zero count, while dates having a buy count show only the buy data.
Demo

Mysql query on a single table

I have a transaction table with datetime, type, measure. I want to produce the day, count, count with measure>20 for type=12.
Not sure how to go about it.
To get the day, count, type I'd write
select date(datetime), count from table where type=12 group by date(datetime)
Just not sure how to add the 3rd col (count with measure>20).
I've thought of trying a left self join, or a corelated subquery.
Appreciate any advice.
Table Name Alerts
Col1 Alert_time (datetime)
Col 2 Alert_type (integer)
Col 3 Measure (integer)
Sample data
2015/01/20 9:00|12|10
2015/01/20 8:00|12|30
2015/01/20 7:00|12|40
2015/01/21 5:00|13|30
2015/01/21 8:00|12|10
Desired Output
2015/01/20|3|2
2015/01/21|1|0
You could perform a count operation on a case expression:
SELECT DATE(datetime),
COUNT(*),
COUNT(CASE WHEN measure > 20 THEN 1 ELSE NULL END)
FROM mytable
WHERE type = 12
GROUP BY date(datetime)
You can have you query like this.
SELECT DATE(datetime),
COUNT(*),
COUNT(CASE WHEN measure > 20 THEN 1 ELSE 0 END)
FROM mytable
WHERE type = 12
GROUP BY date(datetime)

Count and group by number of records in select query result set

I have a table with emp_id, income, etc.
I wish to get number of records for a query like
select * from table_name where income <= 500;
There will be at least 3 such income groups - which will b given at report generation time.
Further I wish to get all 3 Counts - and group the results by the count of their respective income group - all this in a single query.
What is the easiest way to do this?
Can you try this ,if this doesn't suite your need you may need to write a custom stored procedure
SELECT
sum((income <= 500)) as range1,
sum((income <= 1000)) as range2
FROM table_name
sample fiddle
You can use a CASE expression to create your categories, and then a GROUP BY to summarize the categories.
SELECT COUNT(*) AS num,
CASE WHEN income IS NULL THEN 'missing'
WHEN income <= 0 THEN '0'
WHEN income <= 500 THEN '1 - 500'
WHEN income <= 1000 THEN '501-1000'
ELSE '> 1000'
END AS category
FROM table_name
GROUP BY category WITH ROLLUP
Including the WITH ROLLUP clause will give you an overall count as well as the count of each category.