in my mysql database i have instagram_actions_histories table with two important column as action_type and action_name, now i want to use count and case statement to get count of ids column, for example:
count of all ids when action_type equal 1 and action_name equal 'like'
select `account_id`,
count(case when action_type = 1 and action_name='like' then id else 0 END) as `like`,
count(case when action_type = 1 and action_name='superLike' then id else 0 END) as `superLike`,
count(case when action_type = 2 then id else 0 END) as `follow`,
from `instagram_actions_histories`
where `instagram_actions_histories`.`account_id` in (1)
group by `account_id`
unfortunately i get wrong result as all of outputs are same, for example:
account_id like superLike follow
1 1282 1282 1282
correct result was should be:
account_id like superLike follow
1 1282 20 10
You should count 1 for a match, and count NULL when there is no match:
SELECT
account_id,
COUNT(CASE WHEN action_type = 1 AND action_name = 'like' THEN 1 END) AS `like`,
COUNT(CASE WHEN action_type = 1 AND action_name = 'superLike' THEN 1 END) AS superLike,
COUNT(CASE WHEN action_type = 2 THEN 1 END) AS follow
FROM instagram_actions_histories
WHERE account_id IN (1)
GROUP BY account_id;
The problem with the current logic of your CASE expressions is that COUNT will count any non null value as one count. So zero also would be counted.
Note that your current logic would have worked using SUM to take the conditional aggregations, e.g.
SUM(CASE WHEN action_type = 1 AND action_name = 'like'
THEN 1 ELSE 0 END) AS `like`
In this case, to turn off the aggregation for non matching records, we in fact can use zero, because summing zero does not affect the sum.
Related
I have a table 'Item_status_history' with Item id, Status and created_at and associated order_id. This table holds an history of status an item has gone through.
I am trying to query all item_id which have multiple status = "Shipped".
and there must be at least one shipped status after a "Returned".
In the picture we can see the first set and the third set are the desired item_id I need.
The fourth set of data has 2 shipped but they were before "returned".
Note that the statuses are not ordered by created_at. I need to check if there is a shipped status after the return status was sent.
I have tried a few things. I tried to select all the item_id and its id with status shipped and use this in a subquery to again find status with shipped but not equal to the same id. THis is not working
select item_id from item_status_history as ISH where ISH.status = "Shipped" and ISH.item_id in (Select
item_id
from item_status_history
Where status = "Shipped" and item_status_history.id <> ISH.id
)
This i know is only a part of it, then I need to find from the resulting ids, which ones have returned and if it is after the shipped.
Please refer the image below
It must be some unique order of rows , maybe some ID ?
But we can assume, than if Shipped and Retured is in the same time, than condition about Shipped after Returned is fulfilled .
select item_id,
count(case when status='Shipped' then 1 end) number_of_shippments,
max(case when status='Shipped' then created_at end) max_shiped,
max(case when status='Returned' then created_at end) max_returned
from item_status_history
group by item_id
having
count(case when status='Shipped' then 1 end) > 1
and
max(case when status='Shipped' then created_at end) >=
max(case when status='Returned' then created_at end)
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.
here is the structure of my table with data
empId, acitve
1,1
2,0
3,1
45,0
52,1
11,1
I want to know all members count [result is 6]
I want to know active member count (active=1) [result is 4]
I want to know non-active member count (active=0) [result is 2]
But all data should be return in a single row with appropriate column name.
I tried this but its result is not according the expectations:
SELECT Count(*) total, active as total
FROM it_staff_status
GROUP by active
Something like this
select Count(*) total,
sum(active=1) as activemem,
sum(active=0) as inactivemem
from yourtable
TRY THIS: User CASE to count active & inactive and you don't need group by here because all the columns are aggregated by using aggregate function:
SELECT Count(*) total,
SUM(CASE WHEN ACTIVE = 1 THEN 1 ELSE 0 END) as Active,
SUM(CASE WHEN ACTIVE = 0 THEN 1 ELSE 0 END) as InActive
FROM it_staff_status
Try this:
SELECT COUNT(*) as total,
SUM(CASE WHEN active = 1 THEN 1 END) as active_emp,
SUM(CASE WHEN active = 0 THEN 1 END) as inactive_emp
FROM it_staff_status
I have a table "users" where each user has a column "status" containing the value "1" or "2" or "3";
Now with a single mysqli query, I just want to count how many users with status = 1, how many users with status = 2, and how many users with status = 3
NB: The query must be count ONLY and no data has to selected or output except the number of each status.
I found other questions here covering nearly the same topic but they are not exactly the same.
Thanks.
How about this:
SELECT
count(case when status = 1 then 1 else null end) AS STATUS_1,
count(case when status = 2 then 1 else null end) AS STATUS_2,
count(case when status = 3 then 1 else null end) AS STATUS_3
FROM users;
count will only count non-null entries and case-when will work on other databases as well.
This can be done by selectivity counting: just use a CASE expression inside count that only passed a non-null value for the desired rows:
SELECT count(CASE WHEN status = 1 THEN 1 END) status_1
, count(CASE WHEN status = 2 THEN 1 END) status_2
, count(CASE WHEN status = 3 THEN 1 END) status_3
FROM users
This is the same as the nicer, but not nativley supported syntax using FILTER:
SELECT count(*) FILER(WHERE status = 1) status_1
, count(*) FILER(WHERE status = 2) status_2
, count(*) FILER(WHERE status = 3) status_3
FROM users
Read more about this on my website modern SQL: http://modern-sql.com/feature/filter
Use something like this:
SELECT
SUM(IF(value=1,1,0)) as one,
SUM(IF(value=2,1,0)) as two,
SUM(IF(value=3,1,0)) as trhee
FROM users
The IF gives only a '1' back when your values is what you want it to be.
I have a db with several days of data and I want to get the count of each record with Status=0, Status=1...Status=8 for each day. I was to see something like.
DateAndTime State_0 State_1 State_2
2014-08-15 5 8 9
2014-08-16 2 5 6
2014-08-17 4 2 3
I was trying this:
SELECT DISTINCT DATEADD(dd,(DATEDIFF(dd,0,DateAndTime)),0) AS DateAndTime,
SUM( Case When State=0 Then 1 Else 0 End) AS State_0,
SUM( Case When State=1 Then 1 Else 0 End) AS State_1,
SUM( Case When State=2 Then 1 Else 0 End) AS State_2
FROM [DB_002].[dbo].[MyDb]
Group By DateAndTime
Order by DateAndTime
But it keeps adding rows for each state that I add. That is with 3 states I'm getting 4 rows for each date. Any suggestions on how to do this would be greatly appreciated.
You are grouping on the DateAndTime field, which contains also the time component. That makes each record practically unique, so there will be a single record in each group.
Group on the date only:
Group By DATEADD(dd,(DATEDIFF(dd,0,DateAndTime)),0)
Order by DATEADD(dd,(DATEDIFF(dd,0,DateAndTime)),0)
You are confused because you have changed a column and given it the same name as the original column. When you say group by DateAndTime, that is referring to the column in the table. Assuming this is SQL Server, I would write the query as something like this:
SELECT cast(d.DateAndTime as Date) as DateAndNoTime,
SUM(Case When d.State = 0 Then 1 Else 0 End) AS State_0,
SUM(Case When d.State = 1 Then 1 Else 0 End) AS State_1,
SUM(Case When d.State = 2 Then 1 Else 0 End) AS State_2
FROM [DB_002].[dbo].[MyDb] d
Group By cast(d.DateAndTime as Date)
Order by DateAndNoTime;
This changes the name of the alias in the select to avoid confusion. It uses the alias for column references, to also clarify the meaning of the query. And, it uses cast() instead of the old datedd/datediff trick.