Proper grouping using union in MySQL - mysql

I have scanned StackOverflow and the Internet for an answer to the following question but none of the answers I've found works for me. This is my problem.
I have two queries that I want to combine by using UNION. I've managed to combine them as expected but I can't get them to group the way I want. I want the duplicated rows with a redistributed value of 0 to be hidden by ordering the sub queries so that they are prioritized upon grouping... (bad explanation I know - I hope the graphical presentation below explains it better. The rows I want removed are marked with a little arrow in the right margin).
How on earth would I do this?
+-----------+-------+---------------+----------------+----------------------------+
| CANDIDATE | VOTES | RANKED_CHOICE | REDISTRIBUTION | VOTES_AFTER_REDISTRIBUTION |
+-----------+-------+---------------+----------------+----------------------------+
| 1 | 8 | 0 | 0 | 8 |
| 2 | 1 | 6 | -1 | 0 |
| 2 | 1 | 0 | 0 | 1 | >
| 3 | 2 | 0 | 0 | 2 |
| 4 | 4 | 0 | 0 | 4 |
| 5 | 2 | 0 | 0 | 2 |
| 6 | 3 | 0 | 0 | 3 | >
| 6 | 3 | 0 | 1 | 4 |
+-----------+-------+---------------+----------------+----------------------------+
-- The resulting table that's shown on the screen
SELECT vote_candidate candidate, original_votes votes, ranked_choice, redistribution, (original_votes + redistribution) votes_after_redistribution
FROM (
-- Create the first table with original information
SELECT c.vote_candidate, c.original_votes, '0' ranked_choice, '0' redistribution
FROM (
SELECT o.vote_candidate, COUNT(*) original_votes
FROM vote_orders o
WHERE o.vote_order = 1
GROUP BY o.vote_candidate
) c
GROUP BY c.vote_candidate
-- Union a second table containing the second ranked choice of an eliminated candidate and the redistribution.
-- This is done in two steps. In the first step we find out the ranking. In the second step we union the ranked
-- candidate and its' redistribution with each other
UNION
SELECT vote_candidate, original_votes, ranked_choice, redistribution
FROM ((
SELECT vote_candidate, IFNULL(d.original_votes, 0) original_votes, IFNULL(COUNT(*), 0) ranked_choice, (0 - IFNULL(d.original_votes, 0)) redistribution
FROM vote_orders a
-- Get the second favored vote from each eliminated candidates ballots
INNER JOIN (
SELECT vote_id, c, MIN(minimum_vote)
FROM (
SELECT vote_id, vote_candidate c, COUNT(*) minimum_vote
FROM vote_orders
WHERE vote_order = 1
GROUP BY vote_candidate
) t1
WHERE minimum_vote = (
SELECT MIN(minimum_vote)
FROM (
SELECT COUNT(*) minimum_vote
FROM vote_orders
WHERE vote_order = 1
GROUP BY vote_candidate
) t2
)
GROUP BY c
) b
ON a.vote_id = b.vote_id
-- Get the eliminated candidates votes at the beginning of this round
LEFT OUTER JOIN
(
SELECT vote_candidate o, COUNT(*) original_votes
FROM vote_orders
WHERE vote_order = 1
GROUP BY vote_candidate
) d
ON a.vote_candidate = d.o
GROUP BY vote_candidate
ORDER BY redistribution DESC
LIMIT 1
-- Union the candidates redistribution
UNION
(
SELECT vote_candidate, d.original_votes, '0' ranked_choice, (CASE
WHEN IFNULL(d.original_votes, 0) = 0
THEN (0 - IFNULL(d.original_votes, 0))
ELSE (
SELECT MIN(minimum_vote)
FROM (
SELECT vote_candidate c, COUNT(*) minimum_vote
FROM vote_orders
WHERE vote_order = 1
GROUP BY vote_candidate
) t1
WHERE minimum_vote = (
SELECT MIN(minimum_vote)
FROM (
SELECT COUNT(*) minimum_vote
FROM vote_orders
WHERE vote_order = 1
GROUP BY vote_candidate
) t2
)
GROUP BY c
)
END) redistribution
FROM vote_orders a
INNER JOIN (
SELECT vote_id, MIN(minimum_vote)
FROM (
SELECT vote_id, COUNT(*) minimum_vote
FROM vote_orders
WHERE vote_order = 1
GROUP BY vote_candidate
) t1
WHERE minimum_vote = (
SELECT MIN(minimum_vote)
FROM (
SELECT COUNT(*) minimum_vote
FROM vote_orders
WHERE vote_order = 1
GROUP BY vote_candidate
) t2
)
) b
ON a.vote_id = b.vote_id
LEFT OUTER JOIN
(
SELECT vote_candidate o, COUNT(*) original_votes
FROM vote_orders
WHERE vote_order = 1
GROUP BY vote_candidate
) d
ON a.vote_candidate = d.o
-- Determine which candidate to add the redistribution to
WHERE vote_candidate = (
SELECT IFNULL(COUNT(*), 0) ranked_choice
FROM vote_orders a
INNER JOIN (
SELECT vote_id, c, MIN(minimum_vote)
FROM (
SELECT vote_id, vote_candidate c, COUNT(*) minimum_vote
FROM vote_orders
WHERE vote_order = 1
GROUP BY vote_candidate
) t1
WHERE minimum_vote = (
SELECT MIN(minimum_vote)
FROM (
SELECT COUNT(*) minimum_vote
FROM vote_orders
WHERE vote_order = 1
GROUP BY vote_candidate
) t2
)
GROUP BY c
) b
ON a.vote_id = b.vote_id
)
GROUP BY vote_candidate
ORDER BY redistribution DESC
LIMIT 1
)
)) y
) z
-- This is where the grouping fails on me
-- GROUP BY vote_candidate
ORDER BY vote_candidate ASC;
This is the schema:
CREATE TABLE votes
(
vote_id INT NOT NULL AUTO_INCREMENT,
vote_candidate_a INT,
vote_candidate_b INT,
vote_candidate_c INT,
vote_candidate_d INT,
vote_candidate_e INT,
vote_candidate_f INT,
PRIMARY KEY vote_id(vote_id)
);
INSERT INTO votes
VALUES
(NULL, 1, 3, 2, 5, 4, 6),
(NULL, 1, 2, 4, 6, 3, 5),
(NULL, 5, 3, 2, 1, 4, 6),
(NULL, 6, 1, 5, 3, 4, 2),
(NULL, 2, 3, 5, 6, 1, 4),
(NULL, 4, 1, 6, 3, 2, 5),
(NULL, 3, 2, 6, 1, 5, 4),
(NULL, 4, 3, 1, 6, 2, 5),
(NULL, 1, 2, 4, 3, 6, 5),
(NULL, 1, 5, 3, 2, 4, 6),
(NULL, 4, 5, 6, 2, 3, 1),
(NULL, 1, 4, 2, 3, 5, 6),
(NULL, 1, 2, 3, 4, 5, 6),
(NULL, 3, 6, 5, 1, 4, 2),
(NULL, 1, 2, 3, 4, 5, 6),
(NULL, 6, 5, 4, 3, 2, 1),
(NULL, 4, 3, 1, 5, 6, 2),
(NULL, 6, 3, 1, 2, 5, 4),
(NULL, 1, 4, 6, 3, 2, 5),
(NULL, 5, 3, 6, 4, 2, 1);
CREATE TABLE vote_orders
(
id INT NOT NULL AUTO_INCREMENT,
vote_id INT,
vote_order INT,
vote_candidate INT,
PRIMARY KEY id(id)
);
INSERT INTO vote_orders (id, vote_id, vote_order, vote_candidate)
SELECT NULL, vote_id, 1, vote_candidate_a FROM votes
UNION
SELECT NULL, vote_id, 2, vote_candidate_b FROM votes
UNION
SELECT NULL, vote_id, 3, vote_candidate_c FROM votes
UNION
SELECT NULL, vote_id, 4, vote_candidate_d FROM votes
UNION
SELECT NULL, vote_id, 5, vote_candidate_e FROM votes
UNION
SELECT NULL, vote_id, 6, vote_candidate_f FROM votes;

In reality, you already know what the vote_candidate and the number of votes is right off the bat. That is a simple query,
Select vote_candidate, count(*)
From vote_orders
Where vote_order = 1
Group by vote_candidate
This is the foundation of the whole query since the only things missing in the select are the ranked_choice, the redistribution, and the votes_after_redistribution (although this is pretty negligible since it is a calculation from two other columns). So, I would recommend using all of the work you have done to find the two missing columns. Essentially, you should change your query to be a sub-select to find only the missing columns.
I changed your sub-select to only find the ranked_choice and redistribution values if they are not 0. Then, I am grabbing these values (using the left outer join). If there is nothing in the sub-select, then we will default the value to 0.
Your previous query started by defaulting all the values to 0 and then returning the information for the rows that aren't 0. Let's skip all the extra work. Note, I still recommend cleaning up the sub-select, especially since I removed the count of original votes. Some of the joins may be extra since you no longer need to find that information. fiddle: http://sqlfiddle.com/#!2/1b0cb/51
-- The resulting table that's shown on the screen
SELECT v.vote_candidate candidate,
count(*) votes,
IfNull(z.ranked_choice, 0) ranked_choice,
IfNull(z.redistribution, 0) redistribution,
(count(*) + IfNull(z.redistribution, 0)) votes_after_redistribution
FROM vote_orders v left outer join
(
-- Union a second table containing the second ranked choice of an eliminated candidate and the redistribution.
-- This is done in two steps. In the first step we find out the ranking. In the second step we union the ranked
-- candidate and its' redistribution with each other
SELECT vote_candidate, ranked_choice, redistribution
FROM ((
SELECT vote_candidate,
IFNULL(COUNT(*), 0) ranked_choice,
(0 - IFNULL(d.original_votes, 0)) redistribution
FROM vote_orders a
-- Get the second favored vote from each eliminated candidates ballots
INNER JOIN (
SELECT vote_id, vote_candidate c, MIN(minimum_vote)
FROM (
SELECT vote_id, vote_candidate, COUNT(*) minimum_vote
FROM vote_orders
WHERE vote_order = 1
GROUP BY vote_candidate
) t2
) b
ON a.vote_id = b.vote_id
-- Get the eliminated candidates votes at the beginning of this round
LEFT OUTER JOIN
(
SELECT vote_candidate o, COUNT(*) original_votes
FROM vote_orders
WHERE vote_order = 1
GROUP BY vote_candidate
) d
ON a.vote_candidate = d.o
GROUP BY vote_candidate
ORDER BY redistribution DESC
LIMIT 1
-- Union the candidates redistribution
UNION
(
SELECT vote_candidate,
0 ranked_choice,
(CASE
WHEN IFNULL(d.original_votes, 0) = 0
THEN (0 - IFNULL(d.original_votes, 0))
ELSE (
SELECT MIN(minimum_vote)
FROM (
SELECT COUNT(*) minimum_vote
FROM vote_orders
WHERE vote_order = 1
GROUP BY vote_candidate
) t2
)
END) redistribution
FROM vote_orders a
INNER JOIN (
SELECT vote_id, MIN(minimum_vote)
FROM (
SELECT vote_id, COUNT(*) minimum_vote
FROM vote_orders
WHERE vote_order = 1
GROUP BY vote_candidate
) t1
WHERE minimum_vote = (
SELECT MIN(minimum_vote)
FROM (
SELECT COUNT(*) minimum_vote
FROM vote_orders
WHERE vote_order = 1
GROUP BY vote_candidate
) t2
)
) b
ON a.vote_id = b.vote_id
LEFT OUTER JOIN
(
SELECT vote_candidate o, COUNT(*) original_votes
FROM vote_orders
WHERE vote_order = 1
GROUP BY vote_candidate
) d
ON a.vote_candidate = d.o
-- Determine which candidate to add the redistribution to
WHERE vote_candidate = (
SELECT IFNULL(COUNT(*), 0) ranked_choice
FROM vote_orders a
INNER JOIN (
SELECT vote_id, c, MIN(minimum_vote)
FROM (
SELECT vote_id, vote_candidate c, COUNT(*) minimum_vote
FROM vote_orders
WHERE vote_order = 1
GROUP BY vote_candidate
) t1
WHERE minimum_vote = (
SELECT MIN(minimum_vote)
FROM (
SELECT COUNT(*) minimum_vote
FROM vote_orders
WHERE vote_order = 1
GROUP BY vote_candidate
) t2
)
GROUP BY c
) b
ON a.vote_id = b.vote_id
)
GROUP BY vote_candidate
ORDER BY redistribution DESC
LIMIT 1
)
)) y
) z
on v.vote_candidate = z.vote_candidate
Where v.vote_order = 1
GROUP BY v.vote_candidate
ORDER BY v.vote_candidate ASC;

Related

MySQL 5.5 - Select rows where record changes

I have got this table and I need to produce the line with the last A and first B and then last B and first A. Expected results are here:
ID | Date | OPS
6 | 2018-12-20 | A
7 | 2018-12-20 | B
11 | 2018-12-24 | B
12 | 2018-12-25 | A
I have tried to do with the below code with nil luck. I cannot use analytic functions like ROW_NUMBER(), LEAD () or LAG ().
SELECT *
FROM T1
JOIN (
SELECT COUNT(*) cnt, t1.OPS
FROM (
SELECT DISTINCT ts1.OPS
FROM T1 ts1
JOIN T1 tx1 ON ts1.OPS = tx1.OPS
) t1
JOIN (
SELECT DISTINCT ts2.OPS
FROM T1 ts2
JOIN T1 tx2 ON ts2.OPS = tx2.OPS
) t2 on t1.OPS <= t2.OPS
GROUP BY t1.OPS
) tt ON T1.OPS = tt.OPS
My table is:
CREATE TABLE t1(
ID int,
Date1 date,
OPS CHAR
);
INSERT INTO T1 VALUES
( 1, '2018-12-17', 'A'),
( 2, '2018-12-18', 'A'),
( 3, '2018-12-19', 'A'),
( 4, '2018-12-19', 'A'),
( 5, '2018-12-19', 'A'),
( 6, '2018-12-20', 'A'),
( 7, '2018-12-20', 'B'),
( 8, '2018-12-21', 'B'),
( 9, '2018-12-22', 'B'),
(10, '2018-12-23', 'B'),
(11, '2018-12-24', 'B'),
(12, '2018-12-25', 'A'),
(13, '2018-12-26', 'A'),
(14, '2018-12-27', 'A'),
(15, '2018-12-28', 'A');
Without LEAD and LAG and considering that you have multiple records per-date you could use a horrendous simplified solution which assumes that the records are ordered by id instead of date. It does not require ids to be contiguous and works for more than two ops values:
SELECT *
FROM t AS ref
WHERE EXISTS (
SELECT 1
FROM t AS lag
WHERE ops <> ref.ops
AND id < ref.id
AND NOT EXISTS (
SELECT 1
FROM t AS tmp
WHERE id > lag.ID
AND id < ref.id
)
) OR EXISTS (
SELECT 1
FROM t AS led
WHERE ops <> ref.ops
AND id > ref.id
AND NOT EXISTS (
SELECT 1
FROM t AS tmp
WHERE id < led.ID
AND id > ref.id
)
)
Basically for each row, the query searches all previous rows where ids do not match then determines if there is a row in between. It searches forward likewise.
Try the following logic, which phrases your question using a series of unions:
(SELECT ID, Date1, OPS FROM T1 WHERE OPS = 'B' ORDER BY Date1 LIMIT 1)
UNION ALL
(SELECT ID, Date1, OPS FROM T1
WHERE OPS = 'A' AND Date1 <= (SELECT MIN(Date1) FROM T1 WHERE OPS = 'B')
ORDER BY Date1 DESC LIMIT 1)
UNION ALL
(SELECT ID, Date1, OPS FROM T1 WHERE OPS = 'B' ORDER BY Date1 DESC LIMIT 1)
UNION ALL
(SELECT ID, Date1, OPS FROM T1
WHERE OPS = 'A' AND Date1 >= (SELECT MAX(Date1) FROM T1 WHERE OPS = 'B')
ORDER BY Date1 LIMIT 1)
ORDER BY Date1, OPS;
Demo
Note generally that this is a gaps and islands problem. These are very difficult to handle without using analytic functions, which you appear to not have available. If you were using MySQL 8+, then we could phrase a much cleaner looking query.
if we are allowed max and min function, then it is pretty easy to calculate these 4 individually and union the individual results.
-- To select last A
select ID, Date1, OPS
from test_t1
where test_t1.OPS = 'A'
and test_t1.Date1 in
(
select max(t1_a.Date1) as Date1
from
(select ID, Date1, OPS
from test_t1
where OPS = 'A') t1_a inner join
(select min(Date1) as Date1
from test_t1
where OPS = 'B') t1_min_b
on t1_a.Date1 <= t1_min_b.Date1
)
union
-- To select first B
select ID, Date1, OPS
from test_t1
where test_t1.OPS = 'B'
and test_t1.Date1 in
(
select min(Date1) as Date1
from test_t1
where OPS = 'B'
)
union
/*
To select last B
/
select ID, Date1, OPS
from test_t1
where test_t1.OPS = 'B'
and test_t1.Date1 in
(
select max(Date1) as Date1
from test_t1
where OPS = 'B'
)
union
/
To select first A
*/
select ID, Date1, OPS
from test_t1
where test_t1.OPS = 'A'
and test_t1.Date1 in
(
select min(t1_a.Date1) as Date1
from
(select ID, Date1, OPS
from test_t1
where OPS = 'A') t1_a inner join
(select max(Date1) as Date1
from test_t1
where OPS = 'B') t1_max_b
on t1_a.Date1 >= t1_max_b.Date1
)
;
You could use a correlated subquery that computed the min and max ids for each ops, like
SELECT t.*
FROM T1 t
WHERE
t.id IN (
SELECT MIN(id) FROM T1 WHERE ops = t.ops
UNION ALL SELECT MAX(id) FROM T1 WHERE ops = t.ops
)
ORDER BY t.ops, t.id DESC

How to get most occurences of rows for every user in mysql

user_id category suburb dated walk_time
1 experience US 2016-04-09 5
1 discovery US 2016-04-09 5
1 experience UK 2016-04-09 5
1 experience AUS 2016-04-23 10
2 actions IND 2016-04-15 2
2 actions IND 2016-04-15 1
2 discovery US 2016-04-21 2
3 discovery FR 2016-04-12 3
3 Emotions IND 2016-04-23 3
3 discovery UK 2016-04-12 4
3 experience IND 2016-04-12 3
I am trying to get every users most used category,suburb,dated,walk_time
so resulting table would be
user_id category suburb dated walk_time
1 experience US 2016-04-09 5
2 actions IND 2016-04-15 2
3 discovery IND 2016-04-12 3
The query I am trying here is
select user_id,
substring_index(group_concat(suburb order by cnt desc), ',', 1) as suburb_visited,
substring_index(group_concat(category order by cct desc), ',', 1) as category_used,
substring_index(group_concat(walk_time order by wct desc), ',', 1) as walked,
substring_index(group_concat(dated order by nct desc), ',', 1) as dated_at
from (select user_id, suburb, count(*) as cnt,category, count(*) cct, walk_time, count(*) wct, dated,count(*) nct
from temp_user_notes
group by user_id, suburb,category,walk_time,dated
) upv
group by user_id;
SELECT user_id,
(SELECT category FROM temp_user_notes t1
WHERE t1.user_id = T.user_id
GROUP BY category ORDER BY count(*) DESC LIMIT 1) as category,
(SELECT suburb FROM temp_user_notes t2
WHERE t2.user_id = T.user_id
GROUP BY suburb ORDER BY count(*) DESC LIMIT 1) as suburb,
(SELECT dated FROM temp_user_notes t3
WHERE t3.user_id = T.user_id
GROUP BY dated ORDER BY count(*) DESC LIMIT 1) as dated,
(SELECT walk_time FROM temp_user_notes t4
WHERE t4.user_id = T.user_id
GROUP BY walk_time ORDER BY count(*) DESC LIMIT 1) as walk_time
FROM (SELECT DISTINCT user_id FROM temp_user_notes) T
http://sqlfiddle.com/#!9/8aac6a/19
Try this, seems to be a little complicated, but hope help for you;)
Mysql Schema:
CREATE TABLE table1
(`user_id` int, `category` varchar(10), `suburb` varchar(3), `dated` datetime, `walk_time` int)
;
INSERT INTO table1
(`user_id`, `category`, `suburb`, `dated`, `walk_time`)
VALUES
(1, 'experience', 'US', '2016-04-09 00:00:00', 5),
(1, 'discovery', 'US', '2016-04-09 00:00:00', 5),
(1, 'experience', 'UK', '2016-04-09 00:00:00', 5),
(1, 'experience', 'AUS', '2016-04-23 00:00:00', 10),
(2, 'actions', 'IND', '2016-04-15 00:00:00', 2),
(2, 'actions', 'IND', '2016-04-15 00:00:00', 1),
(2, 'discovery', 'US', '2016-04-21 00:00:00', 2),
(3, 'discovery', 'FR', '2016-04-12 00:00:00', 3),
(3, 'Emotions', 'IND', '2016-04-23 00:00:00', 3),
(3, 'discovery', 'UK', '2016-04-12 00:00:00', 4),
(3, 'experience', 'IND', '2016-04-12 00:00:00', 3)
;
Query SQL:
select c.user_id, c.category, s.suburb, d.dated, w.walk_time
from (
select user_id, left(group_concat(category order by cnt desc), locate(',', group_concat(category order by cnt desc)) - 1) as category
from (
select
user_id, category, count(1) as cnt
from table1
group by user_id, category
) t
group by user_id
) c
inner join (
select user_id, left(group_concat(suburb order by cnt desc), locate(',', group_concat(suburb order by cnt desc)) - 1) as suburb
from (
select
user_id, suburb, count(1) as cnt
from table1
group by user_id, suburb
) t
group by user_id
) s on c.user_id = s.user_id
inner join (
select user_id, left(group_concat(dated order by cnt desc), locate(',', group_concat(dated order by cnt desc)) - 1) as dated
from (
select
user_id, dated, count(1) as cnt
from table1
group by user_id, dated
) t
group by user_id
) d on c.user_id = d.user_id
inner join (
select user_id, left(group_concat(walk_time order by cnt desc), locate(',', group_concat(walk_time order by cnt desc)) - 1) as walk_time
from (
select
user_id, walk_time, count(1) as cnt
from table1
group by user_id, walk_time
) t
group by user_id
) w on c.user_id = w.user_id
Result:
| user_id | category | suburb | dated | walk_time |
+---------+------------+--------+---------------------+-----------+
| 1 | experience | US | 2016-04-09 00:00:00 | 5 |
| 2 | actions | IND | 2016-04-15 00:00:00 | 2 |
| 3 | discovery | IND | 2016-04-12 00:00:00 | 3 |

How to get rid of repeated rows?

This is the tables structure I have:
1) When I use this query:
SELECT T_ActionTicketLog.ActionTicketID, T_ActionTicketLog.StatusID
--,case when (dbo.T_ActionTicketLog.StatusID <> 2 and dbo.T_ActionTicketLog.StatusID <> 6) then dbo.T_TicketPrint.TicketBarCode
--else null end as TicketBarCode
FROM T_ActionTicketLog
LEFT OUTER JOIN T_OrderTicket ON T_ActionTicketLog.TicketOrderID = T_OrderTicket.ID
--LEFT OUTER JOIN T_TicketPrint ON T_OrderTicket.ActionTicketID = T_TicketPrint.ActionTicketID and T_OrderTicket.ID = T_TicketPrint.OrderTicketID
where T_ActionTicketLog.ActionTicketID = 24014999
Everything works fine and looks as following:
2) But, when I use this query because I need T_TicketPrint.TicketBarCode:
SELECT T_ActionTicketLog.ActionTicketID, T_ActionTicketLog.StatusID
,case when (dbo.T_ActionTicketLog.StatusID <> 2 and dbo.T_ActionTicketLog.StatusID <> 6) then dbo.T_TicketPrint.TicketBarCode
else null end as TicketBarCode
FROM T_ActionTicketLog
LEFT OUTER JOIN T_OrderTicket ON T_ActionTicketLog.TicketOrderID = T_OrderTicket.ID
LEFT OUTER JOIN T_TicketPrint ON T_OrderTicket.ActionTicketID = T_TicketPrint.ActionTicketID and T_OrderTicket.ID = T_TicketPrint.OrderTicketID
where T_ActionTicketLog.ActionTicketID = 24014999
I've got the extra rows and it looks as following:
Here we have the sold tickets in T_TicketPrint table. When the ticket is sold (status 3) it can be printed out (status 10). The important thing is when we print the ticket, every time new bar code is generated. In this case we have a ticket that was sold and printed out 3 times with 3 different bar codes.
The desired result (other values in TicketBarCode column should be NULL):
How to get rid of extra rows?
This is final correct script:
;WITH cte1
AS (SELECT T_ActionTicketLog.ActionTicketID, T_ActionTicketLog.TicketOrderID, T_ActionTicketLog.StatusID, T_ActionTicketLog.Created,
CASE WHEN StatusID IN (3, 10) THEN 1
ELSE 0
END
* ROW_NUMBER() OVER ( PARTITION BY T_ActionTicketLog.ActionTicketID, T_ActionTicketLog.StatusID ORDER BY T_ActionTicketLog.Created ) AS rn
FROM T_ActionTicketLog
LEFT OUTER JOIN T_OrderTicket ON T_ActionTicketLog.TicketOrderID = T_OrderTicket.ID
),
cte2
AS ( SELECT ActionTicketID , OrderTicketID, TicketBarCode,
ROW_NUMBER() OVER ( PARTITION BY ActionTicketID ORDER BY Created ) AS rn
FROM T_TicketPrint
)
SELECT c1.ActionTicketID, c1.StatusID , oa.TicketBarCode
FROM cte1 c1
OUTER APPLY ( SELECT *
FROM cte2 c2
WHERE c1.ActionTicketID = c2.ActionTicketID AND c1.TicketOrderID = c2.OrderTicketID AND c1.rn = c2.rn
) oa
where c1.ActionTicketID = 24014999
order by c1.Created
If I got you right you need to apply to first pair of(3, 10) the first barcode, second barcode to second pair and so on. But it is not clear which of these pairs is first or second.
This is test script:
DECLARE #actions TABLE
(
TicketID INT ,
StatusID INT
)
DECLARE #prints TABLE
(
TicketID INT ,
BarCode NVARCHAR(MAX)
)
INSERT INTO #actions
VALUES ( 1, 2 ),
( 1, 4 ),
( 1, 3 ),
( 1, 10 ),
( 1, 6 ),
( 1, 3 ),
( 1, 10 ),
( 1, 6 ),
( 1, 3 ),
( 1, 10 ),
( 1, 6 ),
( 1, 7 )
INSERT INTO #prints
VALUES ( 1, 'C1' ),
( 1, 'C2' ),
( 1, 'C3' );
WITH cte1
AS ( SELECT * ,
CASE WHEN StatusID IN ( 3, 10 ) THEN 1
ELSE 0
END
* ROW_NUMBER() OVER ( PARTITION BY TicketID, StatusID ORDER BY StatusID ) AS rn
FROM #actions a
),
cte2
AS ( SELECT * ,
ROW_NUMBER() OVER ( PARTITION BY TicketID ORDER BY BarCode ) AS rn
FROM #prints a
)
SELECT c1.TicketID ,
c1.StatusID ,
oa.BarCode
FROM cte1 c1
OUTER APPLY ( SELECT *
FROM cte2 c2
WHERE c1.TicketID = c2.TicketID
AND c1.rn = c2.rn
) oa
Ouptut:
TicketID StatusID BarCode
1 2 NULL
1 3 C1
1 3 C2
1 3 C3
1 4 NULL
1 6 NULL
1 6 NULL
1 6 NULL
1 7 NULL
1 10 C1
1 10 C2
1 10 C3
Something like:
;WITH cte1
AS ( SELECT T_ActionTicketLog.ActionTicketID, T_ActionTicketLog.OrderTicketID, T_ActionTicketLog.StatusID ,
CASE WHEN StatusID IN ( 3, 10 ) THEN 1
ELSE 0
END
* ROW_NUMBER() OVER ( PARTITION BY T_ActionTicketLog.ActionTicketID, T_ActionTicketLog.StatusID ORDER BY T_ActionTicketLog.Created ) AS rn
FROM T_ActionTicketLog
LEFT OUTER JOIN T_OrderTicket ON T_ActionTicketLog.TicketOrderID = T_OrderTicket.ID
),
cte2
AS ( SELECT ActionTicketID , OrderTicketID, TicketBarCode
ROW_NUMBER() OVER ( PARTITION BY ActionTicketID ORDER BY Created ) AS rn
FROM T_TicketPrint
)
SELECT c1.ActionTicketID, c1.StatusID , oa.TicketBarCode
FROM cte1 c1
OUTER APPLY ( SELECT *
FROM cte2 c2
WHERE c1.ActionTicketID = c2.ActionTicketID AND c1.ID = c2.OrderTicketID AND c1.rn = c2.rn
) oa

MySQL sum plus count in same query

I'm trying to get the rates from anonymous people plus the ones who are registered. They are in different tables.
SELECT product.id, (SUM( users.rate + anonymous.rate ) / COUNT( users.rate + anonymous.rate ))
FROM products AS product
LEFT JOIN users ON users.id_product = product.id
LEFT JOIN anonymous ON anonymous.id_product = product.id
GROUP BY product.id
ORDER BY product.date DESC
So, the tables are like the following:
users-->
id | rate | id_product | id_user
1 2 2 1
2 4 1 1
3 5 2 2
anonymous-->
id | rate | id_product | ip
1 2 2 192..etc
2 4 1 198..etc
3 5 2 201..etc
What I'm trying with my query is: for each product, I would like to have the average of rates. Currently the output is null, but I have values in both tables.
Thanks.
Try like this..
SELECT product.id, (SUM( ifnull(ur.rate,0) + ifnull(ar.rate,0) ) / (COUNT(ur.rate)+Count(ar.rate)))
FROM products AS product
LEFT JOIN users_rate AS ur ON ur.id_product = product.id
LEFT JOIN anonymous_rate AS ar ON ar.id_product = product.id
GROUP BY product.id
Sql Fiddle Demo
First, you are getting a cross join for each product within the table. This is not what you really want. I think this is close to what you are looking for
SELECT p.id,
(coalesce(u.sumrate, 0) + coalesce(a.sumrate, 0)) / coalesce(u.num, 0) + coalesce(a.num, 0))
FROM products p LEFT JOIN
(select id_product, sum(rate) as sumrate, count(*) as num
from users u
group by id_product
) u
ON u.id_product = p.id left join
(select id_product, sum(rate) as sumrate, count(*) as num
from anonymous a
group by id_product
) a
ON a.id_product = p.id
ORDER BY p.date DESC;
Assuming that id is unique in the product table, you don't need an aggregation at the outer level.
You cannot use count and sum on joins if you group by
CREATE TABLE products (id integer);
CREATE TABLE users_rate (id integer, id_product integer, rate integer, id_user integer);
CREATE TABLE anonymous_rate (id integer, id_product integer, rate integer, ip varchar(25));
INSERT INTO products VALUES (1);
INSERT INTO products VALUES (2);
INSERT INTO products VALUES (3);
INSERT INTO products VALUES (4);
INSERT INTO users_rate VALUES(1, 1, 3, 1);
INSERT INTO users_rate VALUES(1, 2, 3, 1);
INSERT INTO users_rate VALUES(1, 3, 3, 1);
INSERT INTO users_rate VALUES(1, 4, 3, 1);
INSERT INTO anonymous_rate VALUES(1, 1, 3, '192..');
INSERT INTO anonymous_rate VALUES(1, 2, 3, '192..');
select p.id,
ifnull(
( ifnull( ( select sum( rate ) from users_rate where id_product = p.id ), 0 ) +
ifnull( ( select sum( rate ) from anonymous_rate where id_product = p.id ), 0 ) )
/
( ifnull( ( select count( rate ) from users_rate where id_product = p.id ), 0 ) +
ifnull( ( select count( rate ) from anonymous_rate where id_product = p.id ), 0 )), 0 )
from products as p
group by p.id
http://sqlfiddle.com/#!2/a2add/8
I've check on sqlfiddle. When there are no rates 0 is given. You may change that.

MySQL SUM DISTINCT with Conditional

I need to gather sums using conditional statements as well as DISTINCT values
with a multiple GROUP BY. The example below is a simplified version of a much much more complex query.
Because the real query is very large, I need to avoid having to drastically re-write the query.
DATA
Contracts
id advertiser_id status
1 1 1
2 2 1
3 3 2
4 1 1
A Query that's close
SELECT
COUNT( DISTINCT advertiser_id ) AS advertiser_qty,
COUNT( DISTINCT id ) AS contract_qty,
SUM( IF( status = 1, 1, 0 ) ) AS current_qty,
SUM( IF( status = 2, 1, 0 ) ) AS expired_qty,
SUM( IF( status = 3, 1, 0 ) ) AS other_qty
FROM (
SELECT * FROM `contracts`
GROUP BY advertiser_id, id
) AS temp
Currently Returns
advertiser_qty contract_qty current_qty expired_qty other_qty
3 4 3 1 0
Needs to Return
advertiser_qty contract_qty current_qty expired_qty other_qty
3 4 2 1 0
Where current_qty is 2 which is the sum of records with status = 1 for only DISTINCT advertiser_ids and each sum function will need the same fix.
I hope someone has a simple solution that can plug into the SUM functions.
-Thanks!!
try this
SELECT
COUNT( DISTINCT advertiser_id ) AS advertiser_qty,
COUNT( DISTINCT id ) AS contract_qty,
(select count(distinct advertiser_id) from contracts where status =1
) AS current_qty,
SUM( IF( status = 2, 1, 0 ) ) AS expired_qty,
SUM( IF( status = 3, 1, 0 ) ) AS other_qty
FROM (
SELECT * FROM `contracts`
GROUP BY advertiser_id, id
) AS temp
DEMO HERE
EDIT:
you may look for this without subselect.
SELECT COUNT(DISTINCT advertiser_id) AS advertiser_qty,
COUNT(DISTINCT id) AS contract_qty,
COUNT(DISTINCT advertiser_id , status = 1) AS current_qty,
SUM(IF(status = 2, 1, 0)) AS expired_qty,
SUM(IF(status = 3, 1, 0)) AS other_qty
FROM (SELECT *
FROM `contracts`
GROUP BY advertiser_id, id) AS temp
DEMO HERE