I am trying to make a query for getting data from 3 tables. Here are the sample tables:
Table 1:
id
user_id
date
amount
1
1
15.01.2021
1000
2
1
16.01.2021
1000
3
2
17.01.2021
1000
4
1
17.01.2021
1000
5
3
22.01.2021
1000
Table 2:
id
user_id
date
amount
1
1
17.01.2021
50
2
1
20.01.2021
50
3
2
21.01.2021
100
4
1
21.01.2021
75
Now I need to make a query that can give the following output using these 2 tables:
Result:
user_id
date
T1.amount
T2.amount
1
15.01.2021
1000
NULL
1
16.01.2021
1000
NULL
1
17.01.2021
1000
50
2
17.01.2021
1000
NULL
1
20.01.2021
NULL
50
2
21.01.2021
NULL
100
1
21.01.2021
NULL
75
3
22.01.2021
1000
NULL
I have tried with Join and Union but no luck. Thanks in Advance
Please try this,
SELECT
A.USER_ID
,A.DATE
,B.AMOUNT AS AMOUNT_1
,C.AMOUNT AS AMOUNT_2
FROM
(
SELECT USER_ID
,DATE
FROM TABLE_1
UNION
SELECT USER_ID
,DATE
FROM TABLE_2
)A
LEFT JOIN
TABLE_1 B
ON A.USER_ID=B.USER_ID AND A.DATE=B.DATE
LEFT JOIN
TABLE_2 C
ON A.USER_ID=C.USER_ID AND A.DATE=C.DATE
GROUP BY A.USER_ID
,A.DATE
, B.AMOUNT
,C.AMOUNT
Union first providing dummy columns then group by
with cte as
(
select user_id,date,amount amt1 , null amt2 from t
union all
select user_id,date,null amt1 , amount amt2 from t1
)
select user_id,date,sum(amt1),sum(amt2)
from cte
group by user_id,date;
Related
I want select rows from my table with last status_Id if there is a row with status_Id = 2 for that rows
ticketStatus_Id ticket_Id status_Id
======================================
1 1 1
2 1 2 -
3 1 3 *
4 2 1
5 3 1
6 3 2 - *
7 4 1
8 4 2 -
9 4 3
10 4 4 *
I want select just rows 3, 6, 10. there are another rows with status_Id = 2 (rows 2, 6, 8) for that ticket_Id,
In other word How to select rows 3,6,10 with ticket_Id =1,3,4 that there are another row with these ticket_Ids and status_Id=2 (rows 2,6,8)
If you want the complete row, then I would view this as exists:
select t.*
from t
where exists (select 1
from t t2
where t2.ticket_id = t.ticket_id and t2.status_id = 2
) and
t.status_Id = (select max(t2.status_id)
from t t2
where t2.ticket_id = t.ticket_id
);
If you just want the ticket_id and status_id (and not the whole row), I would recommend aggregation:
select ticket_id, max(status_id)
from t
group by ticket_id
having sum(status_id = 2) > 0;
In your case, ticketStatus_Id seems to increase with status_id, so you can use:
select max(ticketStatus_Id) as ticketStatus_Id, ticket_id, max(status_id) as Status_Id
from t
group by ticket_id
having sum(status_id = 2) > 0;
First, for each ticket we get the row with the highest status. We can do this with a self-join. Each row is joined with the row with the next highest status. We select the rows which have no higher status, those will be the highest. Here's a more detailed explanation.
select ts1.*
from ticket_statuses ts1
left outer join ticket_statuses ts2
on ts1.ticket_Id = ts2.ticket_Id
and ts1.status_Id < ts2.status_Id
where ts2.ticketStatus_Id is null
3 1 3
4 2 1
6 3 2
10 4 4
11 5 3
Note that I've added a curve-ball of 11, 5, 3 to ensure we only select tickets with a status of 2, not greater than 2.
Then we can use that as a CTE (or subquery if you're not using MySQL 8) and select only those tickets who have a status of 2.
with max_statuses as (
select ts1.*
from ticket_statuses ts1
left outer join ticket_statuses ts2
on ts1.ticket_Id = ts2.ticket_Id
and ts1.status_Id < ts2.status_Id
where ts2.ticketStatus_Id is null
)
select ms.*
from max_statuses ms
join ticket_statuses ts
on ms.ticket_id = ts.ticket_id
and ts.status_id = 2;
3 1 3
6 3 2
10 4 4
This approach ensures we select the complete rows with the highest statuses and any extra data they may contain.
dbfiddle
This is basicaly a "last row per group" problem. You will find some solutions here. My prefered solution would be:
select t.*
from (
select max(ticketStatus_Id) as ticketStatus_Id
from mytable
group by ticket_Id
) tmax
join mytable t using(ticketStatus_Id)
The difference in your question is that you have a condition requiring a specific value within the group. This can be solved with a JOIN within the subquery:
select t.*
from (
select max(t1.ticketStatus_Id) as ticketStatus_Id
from mytable t2
join mytable t1 using(ticket_Id)
where t2.status_Id = 2
group by t2.ticket_Id
) tmax
join mytable t using(ticketStatus_Id)
Result:
| ticketStatus_Id | ticket_Id | status_Id |
| --------------- | --------- | --------- |
| 3 | 1 | 3 |
| 6 | 3 | 2 |
| 10 | 4 | 4 |
View on DB Fiddle
A solution using window functions could be:
select ticketStatus_Id, ticket_Id, status_Id
from (
select *
, row_number() over (partition by ticket_Id order by ticketStatus_Id desc) as rn
, bit_or(status_Id = 2) over (partition by ticket_Id) > 0 as has_status2
from mytable
) x
where has_status2 and rn = 1
A quite expressive way is to use EXISTS and NOT EXISTS subquery conditions:
select t.*
from mytable t
where exists (
select *
from mytable t1
where t1.ticket_Id = t.ticket_Id
and t1.status_Id = 2
)
and not exists (
select *
from mytable t1
where t1.ticket_Id = t.ticket_Id
and t1.ticketStatus_Id > t.ticketStatus_Id
)
SELECT a.*
FROM t a
JOIN
(
SELECT ticket_id, MAX(status_id) max_status_id
FROM t
WHERE status_id >= 2
GROUP BY ticket_id
) b
ON a.ticket_id = b.ticket_id
AND a.status_id = b.max_status_id;
SELECT
MAX(m1.ticketstatus_Id) as ticket_status,
m1.ticket_Id as ticket,
MAX(m1.status_Id) as status
FROM mytable m1
WHERE
m1.ticket_Id in (select m2.ticket_Id from mytable m2 where m2.ticket_Id=m1.ticket_Id and m2.status_Id=2)
GROUP BY m1.ticket_Id
I have two tables like below
Table 1 :
Id User_id rating1 rating2 rating3
1 4 4 3 5
2 4 5 2 2
3 1 5 5 5
4 4 1 2 3
Table 2 :
Id User rating1 rating2 rating3 comment
1 1 5 2 1 okay
2 4 2 4 4 good
I want to get the count of user_id who having rating1 value between 1 , 2 , 3, 4, 5 and who having rating2 value between 1 , 2 , 3, 4, 5 and who having rating3 value between 1 , 2 , 3, 4, 5
I am trying the following way ,
select count(Table1.User_id)
from Table1
join Table2
on Table1.User_id = Table2.User
where Table1.rating1 = ( 1 || 2 || 3 || 4 || 5)
and Table2.rating1 = ( 1 || 2 || 3 || 4 || 5 )
But it's not working.
based on your comment, your query is very far from your desire result.
You need to pivot your table to be a single column
SELECT rating, count(*)
FROM (
SELECT `User_id` , `rating1` as rating FROM Table1
UNION ALL
SELECT `User_id` , `rating2` as rating FROM Table1
UNION ALL
SELECT `User_id` , `rating3` as rating FROM Table1
UNION ALL
SELECT `User` , `rating1` as rating FROM Table2
UNION ALL
SELECT `User` , `rating2` as rating FROM Table2
UNION ALL
SELECT `User` , `rating3` as rating FROM Table2
) as combine
GROUP BY rating
ORDER BY rating
Use IN (1,2,3,4,5) Insted of = (1,2,3,4,5)
It will give error of multiple oprands since equal can be use when it comes to compare one on one value.
SELECT t1.user_id,t2.user,count(*) as Count_Rate
FROM
(SELECT * FROM table1 WHERE rating1 IN ('1','2','3','4','5')) as t1
JOIN
(SELECT * FROM table2 WHERE rating1 IN ('1','2','3','4','5')) as t2
on t1.user_id = t2.user
GROUP by user_id
Or You can try this also by adding,
(SELECT *,count(rating1='1') AS rate1 FROM table1 WHERE rating1 IN ('1','2','3','4','5')) as t1
My tables:
t1
col_a col_b
1 100
1 200
1 300
2 400
t2
col_a col_b
100 5
100 6
t3
col_a col_b
5 100
6 200
6 300
If I run a query and left join the 3 tables in order i get:
1 100 5 100
1 100 6 200
1 100 6 300
1 200 null null
1 300 null null
2 400 null null
If I add group by t1.col_a, t2.col_b:
1 100 5 100
1 100 6 (200 or 300)
1 (200 or 300) null null
2 400 null null
But I don't want to the 3rd row to show because it does not have a value in t2.col_b. I could add a condition to where that column is not null, but that would remove the last row which needs to stay.
In a perfect query I would like to see:
1 100 5 100
1 100 6 (200 or 300)
2 400 null null
Try this one. t1.col_b is a little tricky, because if multiple t1.col_a values exists with none reference to t2, this query select a random for this.
SELECT
sub.col_a,
IFNULL(sub.relation,t1.col_b),
t2.col_b,
t3.col_b
FROM(
SELECT
t1.col_a,
group_concat(DISTINCT t2.col_a) AS relation
FROM table1 AS t1
LEFT JOIN table2 AS t2 ON t2.col_a = t1.col_b
GROUP BY t1.col_a
) AS sub
LEFT JOIN table1 AS t1
ON t1.col_a = sub.col_a
AND (t1.col_b IN (sub.relation) OR sub.relation IS NULL)
LEFT JOIN table2 AS t2
ON t2.col_a = t1.col_b
LEFT JOIN table3 AS t3
ON t3.col_a = t2.col_b
GROUP BY t1.col_a, t2.col_b
One approach is to select only the non-null rows as a part of the result, then union it with a broader - but grouped - set of results:
SELECT * FROM t1
JOIN t2 ON t1.col_b = t2.col_a
LEFT JOIN t3 ON t2.col_b = t3.col_a
UNION
SELECT * FROM t1
LEFT JOIN t2 ON t1.col_b = t2.col_a
LEFT JOIN t3 ON t2.col_b = t3.col_a
GROUP BY t1.col_a
Notice that the first query does not have a LEFT JOIN. That's because we don't want any NULL results in that query. That query gives you:
t1.col_a t2.col_a t3.col_a t3.col_b
1 100 5 100
1 100 6 200
1 100 6 300
And the second query gives you:
t1.col_a t2.col_a t3.col_a t3.col_b
1 100 5 100
2 400 NULL NULL
Finally, when you do a UNION, it gets rid of the duplicate result (first row in first result set is equal to the first row in the second result set). This is the default behavior of the UNION command, resulting in:
t1.col_a t2.col_a t3.col_a t3.col_b
1 100 5 100
1 100 6 200
1 100 6 300
2 400 NULL NULL
I have a two table and I have left join and want sum of value from both table.
But the result from second table not retrieved successfully (SUM not group by properly)
Table test1
id redeem_count cost camp_id
1 2 500 1
2 3 1000 1
3 2 2000 2
4 2 3000 2
5 2 1200 3
Table test2
id bill_amount earning test1_id
1 4000 50 1
2 5000 55 3
3 6000 60 4
Output
camp_id redeem cost bill earning
1 5 1500 15000 165
2 4 5000 NULL NULL
3 2 1200 NULL NULL
Desired Result
camp_id redeem cost bill earning
1 5 1500 4000 50
2 4 5000 11000 115
3 2 1200 0 0
I have execute following SQL.
SELECT
t1.camp_id,
t1.redeem,
t1.cost,
t2.bill,
t2.earning
FROM (SELECT COALESCE(SUM(redeem_count),0) AS redeem, COALESCE(SUM(cost),0) AS cost, id,camp_id FROM test1 GROUP BY camp_id) t1
LEFT JOIN(SELECT COALESCE(SUM(bill_amount),0) AS bill, COALESCE(SUM(earning),0) AS earning, test1_id FROM test2) t2
ON t1.id = t2.test1_id
How can I achieve desired result? Would you please provide the solution?
SELECT
t1.camp_id,
t1.redeem,
t1.cost,
t2.bill,
t2.earning
FROM (SELECT COALESCE(SUM(redeem_count),0) AS redeem, COALESCE(SUM(cost),0) AS cost, id,camp_id FROM test1 GROUP BY camp_id) t1
LEFT JOIN(SELECT COALESCE(bill_amount) AS bill, COALESCE(earning) AS earning, test1_id FROM test2) t2
ON t1.id = t2.test1_id
try this:
SELECT
t1.camp_id,
t1.redeem,
t1.cost,
t2.bill,
t2.earning
FROM (SELECT COALESCE(SUM(redeem_count),0) AS redeem, COALESCE(SUM(cost),0) AS cost, id,camp_id FROM test1 GROUP BY camp_id) t1
LEFT JOIN(SELECT COALESCE(bill_amount) AS bill, COALESCE(earning) AS earning, test1_id FROM test2) t2
ON t1.id = t2.test1_id
update:1
SELECT
t1.camp_id,
t1.redeem,
t1.cost,
t2.bill,
t2.earning
FROM
(
SELECT
COALESCE(SUM(redeem_count),0) AS redeem,
COALESCE(SUM(cost),0) AS cost,
id,
camp_id
FROM test1
GROUP BY camp_id) t1
LEFT JOIN(
SELECT
SUM(bill_amount) AS bill,
SUM(earning) AS earning, test1_id FROM test2 GROUP BY test1_id) t2
ON t1.camp_id= t2.test1_id
My code produced output like:
camp_id redeem cost bill earning
1 5 1500 4000 50
2 4 5000 11000 115
3 2 1200 (NULL) (NULL)
EDITED
Create your subquery like when you want to query only the required result, then join those results.
(Query is not tested!)
SELECT
X1.camp_id
, X1.redeem
, X1.redeem
, X2.cost
, X2.earning
FROM
(
SELECT
SUM(redeem_count) AS redeem,
SUM(cost) AS cost,
id,
camp_id
FROM
test1
GROUP BY
camp_id
) X1
LEFT JOIN (
SELECT
T1.camp_id,
SUM(T2.bill_amount) AS bill,
SUM(T2.earning) AS earning
FROM
test1 T1
INNER JOIN test2 T2
ON T1.id = T2.id
GROUP BY
T1.camp_id
) X2
ON X1.camp_id = X2.camp_id
I am writing a query to grab the items that a specific user_id was the first to use. Here is some sample data -
item_id used_user_id date_used
1 1 2012-08-25
1 2 2012-08-26
1 3 2012-08-27
2 2 2012-08-27
3 1 2012-08-27
4 1 2012-08-21
4 3 2012-08-24
5 3 2012-08-23
query
select item_id as inner_item_id, ( select used_user_id
from test
where test.item_id = inner_item_id
order by date_used asc
limit 1 ) as first_to_use_it
from test
where used_user_id = 1
group by item_id
It returns the correct values
inner_item_id first_to_use_it
1 1
3 1
4 1
but the query is VERY slow on a giant table. Is there a certain index that I can use or a better query that I can write?
i can't get exactly what you mean because in your inner query you have sorted it by their used_user_id and and on your outer query you have filtered it also by their userid. Why not do this directly?
SELECT DISTINCT item_id AS inner_item_id,
used_user_id AS first_to_use_it
FROM test
WHERE used_user_id = 1
UPDATE 1
SELECT b.item_id,
b.used_user_id AS first_to_use_it
FROM
(
SELECT item_ID, MIN(date_used) minDate
FROM tableName
GROUP BY item_ID
) a
INNER JOIN tableName b
ON a.item_ID = b.item_ID AND
a.minDate = b.date_used
WHERE b.used_user_id = 1