I have a table called votes with 4 columns: id, name, choice, date.
****id****name****vote******date***
****1*****sam*******A******01-01-17
****2*****sam*******B******01-05-30
****3*****jon*******A******01-01-19
My ultimate goal is to count up all the votes, but I only want to count 1 vote per person, and specifically each person's most recent vote.
In the example above, the result should be 1 vote for A, and 1 vote for B.
Here is what I currently have:
select name,
sum(case when uniques.choice = A then 1 else 0 end) votesA,
sum(case when uniques.choice = B then 1 else 0 end) votesB
FROM (
SELECT id, name, choice, max(date)
FROM votes
GROUP BY name
) uniques;
However, this doesn't work because the subquery is indeed selecting the max date, but it's not including the correct choice that is associated with that max date.
Don't think "group by" to get the most recent vote. Think of join or some other option. Here is one way:
SELECT v.name,
SUM(v.choice = 'A') as votesA,
SUM(v.choice = 'B') as votesB
FROM votes v
WHERE v.date = (SELECT MAX(v2.date) FROM votes v2 WHERE v2.name = v.name)
GROUP BY v.name;
Here is a SQL Fiddle.
Your answer are close but need to JOIN self
Subquery get Max date by name then JOIN self.
select
sum(case when T.vote = 'A' then 1 else 0 end) votesA,
sum(case when T.vote = 'B' then 1 else 0 end) votesB
FROM (
SELECT name,Max(date) as date
FROM T
GROUP BY name
) AS T1 INNER JOIN T ON T1.date = T.date
SQLFiddle
Try this
SELECT
choice,
COUNT(1)
FROM
votes v
INNER JOIN
(
SELECT
id,
max(date)
FROM
votes
GROUP BY
name
) tmp ON
v.id = tmp.id
GROUP BY
choice;
Something like this (if you really need count only last vote of person)
SELECT
sum(case when vote='A' then cnt else 0 end) voteA,
sum(case when vote='B' then cnt else 0 end) voteB
FROM
(SELECT vote,count(distinct name) cnt
FROM (
SELECT name,vote,date,max(date) over (partition by name) maxd
FROM votes
)
WHERE date=maxd
GROUP BY vote
)
PS. MySQL v 8
select
name,
sum( case when choice = 'A' then 1 else 0 end) voteA,
sum( case when choice = 'B' then 1 else 0 end) voteB
from
(
select id, name, choice
from votes
where date = (select max(date) from votes t2
where t2.name = votes.name )
) t
group by name
Or output just one row for the total counts of VoteA and VoteB:
select
sum( case when choice = 'A' then 1 else 0 end) voteA,
sum( case when choice = 'B' then 1 else 0 end) voteB
from
(
select id, name, choice
from votes
where date = (select max(date) from votes t2
where t2.name = votes.name )
) t
Based on #d-shish solution, and since introduction (in MySQL 5.7) of ONLY_FULL_GROUP_BY, the GROUP BY statement must be placed in subquery like this :
SELECT v.`name`,
SUM(v.`choice` = 'A') as `votesA`,
SUM(v.`choice` = 'B') as `votesB`
FROM `votes` v
WHERE (
SELECT MAX(v2.`date`)
FROM `votes` v2
WHERE v2.`name` = v.`name`
GROUP BY v.`name` # << after
) = v.`date`
# GROUP BY v.`name` << before
Otherwise, it won't work anymore !
Related
I have an SQL Query that used to have inclusive <> '1' as a clause but I had to remove it to ensure I pick up all the data but I still need to count the value of the inclusive field in the SUM
Is it possible to make the SUM(customer_cost) as customer_total not include rows where inclusive <> '1'
$callChargesSql = "SELECT
customer,
source,
source_name,
calltype,
SUM(customer_cost) as customer_total,
SUM(cost) as cost,
SUM(recording_cost) as recording_cost,
SUM(recording_customer) as recording_customer
FROM
billing_calldata
WHERE
(
customer = '".db_string($result["sequence"])."' OR
customer IN
(
SELECT
sequence
FROM
customer
WHERE
resellerid = '".db_string($result["sequence"])."'
)
) AND
(
(
MONTH(timestamp) = '".db_string($calls["month"])."' AND
YEAR(timestamp) = '".db_string($calls["year"])."'
) OR
status = 'y'
)
GROUP BY customer, source, calltype
ORDER BY customer, timestamp ASC;";
In MySQL you can use IF statement within SUM operator like:
SUM(IF(inclusive = '1',customer_cost, 0)) as customer_total
Below simplified example:
select
customer_id,
sum(customer_cost) as total_customer_cost,
sum(if(inclusive=1, customer_cost, 0)) as inclusive_customer_cost
from billing_calldata
group by customer_id
;
Here you can test it: SQLize.online
In my test you can
You can use something like this: SUM(case when inclusive = '1' then customer_cost else 0 end) as customer_total
In your example query:
$callChargesSql = "SELECT
customer,
source,
source_name,
calltype,
SUM(case when inclusive = '1' then customer_cost else 0 end) as customer_total,
SUM(cost) as cost,
SUM(recording_cost) as recording_cost,
SUM(recording_customer) as recording_customer
FROM
billing_calldata
WHERE
(
customer = '".db_string($result["sequence"])."' OR
customer IN
(
SELECT
sequence
FROM
customer
WHERE
resellerid = '".db_string($result["sequence"])."'
)
) AND
(
(
MONTH(timestamp) = '".db_string($calls["month"])."' AND
YEAR(timestamp) = '".db_string($calls["year"])."'
) OR
status = 'y'
)
GROUP BY customer, source, calltype
ORDER BY customer, timestamp ASC;";
I'm trying to select if a user rating (user.rating) is greater then 6 or if the user has more then 100 transactions (transaction table count). Basically count how many transactions the user has then where (transaction count >= 100 OR user rating >= 6).
SELECT *
FROM `user`
JOIN (SELECT COUNT(*)
FROM transaction
WHERE transaction.user_id=user.id
AND type='L'
AND status='S') AS tcount
WHERE (user.rating >= '6' OR tcount >= '100')
Just another possible answer. I've created simplified schemas to test it, please try it and let me know the result.
SELECT *
FROM user
WHERE user.rating >= 6 OR (SELECT COUNT(*) FROM transaction WHERE user_id = user.id and type = 'L' and status = 'S') >= 100;
Use an alias on COUNT(*)
SELECT *
FROM `user`
JOIN (SELECT user_id, COUNT(*) cnt
FROM transaction
WHERE type='L'
AND status='S'
GROUP BY user_id) AS tcount
ON user.id = tcount.user_id
WHERE (user.rating >= '6' OR tcount.cnt >= '100')
You can write that without the subquery, like this
SELECT u.id
FROM `user` u
JOIN `transaction` t
ON t.user_id=u.id
WHERE t.type = 'L' AND t.status = 'S'
GROUP BY u.id
HAVING sum(case when u.rating >= 6 then 1 end) > 0 OR count(*) >= 100
I have a query which works great for 1000 records or less but now I need to optimize it for 50,000+ records and when I run it on that it just stalls...
Here is my code:
SELECT
b1.account_num,b1.effective_date as ed1,b1.amount as am1,
b2.effective_date as ed2,b2.amount as am2
FROM bill b1
left join bill b2 on (b1.account_num=b2.account_num)
where b1.effective_date = (select max(effective_date) from bill where account_num = b1.account_num)
and (b2.effective_date = (select max(effective_date) from bill where account_num = b1.account_num and effective_date < (select max(effective_date) from bill where account_num = b1.account_num)) or b2.effective_date is null)
ORDER BY b1.effective_date DESC
My objective is to get the latest two effective dates and amounts from one table with many records.
Here is a working answer from your SQL-Fiddle baseline
First, the inner preQuery gets the max date per account. That is then joined to the bill table per account AND the effective date is less than the max already detected.
That is then joined to each respective bill for their amounts.
select
FB1.account_num,
FB1.effective_date as ed1,
FB1.amount as am1,
FB2.effective_date as ed2,
FB2.amount as am2
from
( select
pq1.account_num,
pq1.latestBill,
max( b2.effective_date ) as secondLastBill
from
( SELECT
b1.account_num,
max( b1.effective_date ) latestBill
from
bill b1
group by
b1.account_num ) pq1
LEFT JOIN bill b2
on pq1.account_num = b2.account_num
AND b2.effective_date < pq1.latestBill
group by
pq1.account_num ) Final
JOIN Bill FB1
on Final.Account_Num = FB1.Account_Num
AND Final.LatestBill = FB1.Effective_Date
LEFT JOIN Bill FB2
on Final.Account_Num = FB2.Account_Num
AND Final.secondLastBill = FB2.Effective_Date
ORDER BY
Final.latestBill DESC
In mysql , window analytic function like row_number is not there, so we can simulate the same using variables.
The good thing is, the table is scanned only once with this approach.
A row_number is assigned to each partition which is divided based on ( account number, effective date ) and only 2 rows are selected from each partition.
select account_num,
max(case when row_number =1 then effective_date end) as ed1,
max(case when row_number =1 then amount end) as am1,
max(case when row_number =2 then effective_date end) as ed2,
max(case when row_number =2 then amount end )as am2
from (
select account_num, effective_date, amount,
#num := if(#prevacct= account_num , #num + 1, 1) as row_number,
#prevacct := account_num as dummy
from bill, (select #num:=0, #prevacct := '' ) as var
order by account_num , effective_date desc
)T
where row_number <=2
group by account_num
I have this query which does some calculations based on some derived tables that are linked with an INNER JOIN.
At the moment I have a WHERE clause which pulls out one id at a time. But how can I make it list all the ids?
I have tried GROUP BY in various places but can't figure it out.
My query so far is as follows:
SELECT
equipment_id,
service_duration,
available_duration,
(available_duration / service_duration)*100 AS availability
FROM (
SELECT
SUM(service_end_time - service_start_time) AS service_duration
FROM(
SELECT equipment_id,
(CASE
END) AS service_start_time,
(CASE
END) AS service_end_time
FROM t1
WHERE equipment_id = 'EX123'
)AS A
) AS B
JOIN(
SELECT equipment_id,
SUM(available_end_time - available_start_time) AS available_duration
FROM (
SELECT equipment_id,
(CASE
END) AS available_start_time,
(CASE
END) AS available_end_time
FROM t2
WHERE equipment_id = 'EX123'
) AS C
) AS D
ON equipment_id=D.equipment_id
What I want to do is replace the WHERE clause with a GROUP BY to list all the ids, or similar, but getting that to work is beyond my skill level... Any help greatly appreciated :)
Try below:
SELECT
equipment_id, service_duration, available_duration,
(available_duration / service_duration)*100 AS availability
FROM
(
SELECT equipment_id,
SUM(service_end_time - service_start_time) AS service_duration
FROM
(
SELECT equipment_id,
(CASE ... END) AS service_start_time,
(CASE ... END) AS service_end_time
FROM t1
) AS A
GROUP BY equipment_id
) AS B
JOIN
(
SELECT equipment_id,
SUM(available_end_time - available_start_time) AS available_duration
FROM
(
SELECT equipment_id,
(CASE ... END) AS available_start_time,
(CASE ... END) AS available_end_time
FROM t2
) AS C
GROUP BY equipment_id
) AS D
ON equipment_id=D.equipment_id
Try this (replace my field names with your field names):
SELECT
a.emp_id,
service_duration,
available_duration
FROM
(
SELECT
emp_id,
SUM(service_end_time - service_start_time) AS service_duration
FROM
data
GROUP BY
emp_id
) a
JOIN
(
SELECT
emp_id,
SUM(available_end_time - available_start_time) AS available_duration
FROM
data
GROUP BY
emp_id
) b
ON a.emp_id = b.emp_id
GROUP BY
a.emp_id
My table contains votes of users for different items. It has the following columns:
id, user_id, item_id, vote, utc_time
Only id is a unique field and the combination of user_id and utc_time is probably also unique. But user can cast votes for any item many times.
A vote is not a number but rather has one of several possible values (e.g., "awful", "bad", "good", "excellent").
I need to count how many different users cast their last vote for a given #item# as "excellent", as "good", etc. So assuming I have only four different possible vote values, I need to get four records with the following fields:
vote, count_of_users
I understand how to count all votes, not only last votes of users:
SELECT vote, COUNT(id) FROM votes WHERE item_id=#item# GROUP BY vote;
But I cannot figure out how to count only the votes where utc_time = MAX(utc_time) for each user... Thanks for your help.
This question is connected to the previous question of mine: Select one row with MAX(column) for known other several columns without subquery
try this solution if it fits with you,
SELECT a.item_ID,
SUM(CASE WHEN a.vote = 'awful' THEN 1 ELSE 0 END) awful,
SUM(CASE WHEN a.vote = 'bad' THEN 1 ELSE 0 END) bad,
SUM(CASE WHEN a.vote = 'good' THEN 1 ELSE 0 END) good,
SUM(CASE WHEN a.vote = 'excellent' THEN 1 ELSE 0 END) excellent
FROM tableName a
INNER JOIN
(
SELECT user_ID, MAX(utc_time) max_time
FROM tableName
GROUP BY user_ID
) b ON a.user_ID = b.user_ID AND
a.utc_time = b.max_time
-- WHERE a.item_ID = 'valueHere'
GROUP BY a.item_ID
UPDATE 1
SELECT a.item_ID,
a.vote,
COUNT(*) totalCount
FROM tableName a
INNER JOIN
(
SELECT user_ID, MAX(utc_time) max_time
FROM tableName
WHERE item_id = 'valueHere'
GROUP BY user_ID
) b ON a.user_ID = b.user_ID AND
a.utc_time = b.max_time
GROUP BY a.vote