I already know i can do outer join in MySQL using union.
I also check this one.
Full Outer Join in MySQL
But i want to do something like this and I don't know how can I achieve this using union.
I have db table user_count as follow.
+-------------+-----------+---------+-------+
| meb_id | left_id |right_id |active |
+-------------+-----------+---------+-------+
| 1001 | (NULL) | (NULL) | 1 |
| 1002 | 1001 | 0 | 0 |
| 1003 | 0 | 1001 | 0 |
| 1004 | 1001 | 0 | 0 |
| 1004 | 1002 | 0 | 0 |
+-------------+-----------+---------+-------+
I have queries as follow.
SELECT left_id, COUNT(left_id) AS left_count FROM `user_count` GROUP BY left_id
SELECT right_id, COUNT(right_id) AS right_count FROM `user_count` GROUP BY right_id;
SELECT left_id AS meb_id, COUNT(left_id) AS active_left_count FROM `user_count` WHERE active = 1 GROUP BY left_id;
SELECT right_id AS meb_id, COUNT(right_id) AS active_right_count FROM `user_count` WHERE active = 1 GROUP BY right_id;
I want to preform outer join or union so my result will be like this
+-------------+-----------+------------+------------+--------------+
| meb_id |left_count |right_count |active_left |active_right |
+-------------+-----------+------------+------------+--------------+
| (NULL) | 0 | 0 | 0 | 0 |
| 0 | 1 | 3 | 0 | 0 |
| 1001 | 2 | 1 | 0 | 0 |
| 1002 | 1 | 0 | 0 | 0 |
| 1003 | 0 | 0 | 0 | 0 |
+-------------+-----------+------------+------------+--------------+
How can i do this. Any help greatly appreciated.
Try this:
select
a.meb_id
,sum(case when a.meb_id = b.left_id then 1 else 0 end) left_count
,sum(case when a.meb_id = b.right_id then 1 else 0 end) right_count
,sum(case when a.meb_id = b.left_id and active = 1 then 1 else 0 end) active_left
,sum(case when a.meb_id = b.right_id and active = 1 then 1 else 0 end) active_right
from (
select meb_id from user_count union
select left_id from user_count union
select right_id from user_count
) a
cross join user_count b
group by a.meb_id
order by a.meb_id
Demo
sqlfiddle
Related
I run a database that keeps information on rugby referees. I want to show all the teams that a referee has refereed, and the stats from the games. I have the MySQL query returning information, but only when a team is the home team, where as I want it to sum all the results and give them to me as a combined figure.
This is my Query:
SELECT
DISTINCT *
FROM (
SELECT
T1.Name AS Team,
COUNT(M.REF) AS RefCount,
SUM(M.HTries) AS Tries,
SUM(M.HPT) AS PT,
SUM(M.HConv) AS Conv,
SUM(M.HPG) AS PG,
SUM(M.HDG) AS DG,
SUM(M.HYC) AS YC,
SUM(M.HRC) AS RC
FROM matches M
LEFT JOIN teams T1 ON T1.TeamID=M.HTeam
WHERE M.Ref = 2 AND M.date < CURDATE()
GROUP BY T1.TeamID
UNION
SELECT
T2.Name as Team,
COUNT(M.REF) AS RefCount,
SUM(M.ATries) AS Tries,
SUM(M.APT) AS PT,
SUM(M.AConv) AS Conv,
SUM(M.APG) AS PG,
SUM(M.ADG) AS DG,
SUM(M.AYC) AS YC,
SUM(M.ARC) AS RC
FROM matches M
LEFT JOIN teams T2 ON T2.TeamID=M.ATeam
WHERE M.Ref = 2 AND M.date < CURDATE()
GROUP BY T2.TeamID) AS Teams
GROUP BY Team
ORDER BY RefCount DESC, Team ASC
What it provides is this:
+--------------+----------+-------+------+------+------+------+------+------+
| Team | RefCount | Tries | PT | Conv | PG | DG | YC | RC |
+--------------+----------+-------+------+------+------+------+------+------+
| France | 4 | 3 | 0 | 3 | 6 | 0 | 1 | 0 |
| Argentina | 2 | 0 | 0 | 0 | 0 | 0 | 1 | 0 |
| Australia | 2 | 5 | 0 | 4 | 1 | 0 | 2 | 0 |
| Ireland | 2 | 2 | 0 | 2 | 4 | 0 | 0 | 0 |
| Wales | 2 | 4 | 0 | 3 | 5 | 0 | 0 | 0 |
| Barbarians | 1 | 0 | 0 | 0 | 0 | 0 | 0 | 0 |
| Chiefs | 1 | 0 | 0 | 0 | 0 | 0 | 0 | 0 |
| Chile | 1 | 0 | 0 | 0 | 0 | 0 | 0 | 0 |
| Hungary | 1 | 0 | 0 | 0 | 0 | 0 | 0 | 0 |
| Italy | 1 | 0 | 0 | 0 | 0 | 0 | 0 | 0 |
| New Zealand | 1 | 5 | 0 | 4 | 2 | 0 | 0 | 0 |
| Scotland | 1 | 0 | 0 | 0 | 0 | 0 | 0 | 0 |
| South Africa | 1 | 0 | 0 | 0 | 4 | 2 | 0 | 0 |
| Ukraine | 1 | 0 | 0 | 0 | 0 | 0 | 0 | 0 |
+--------------+----------+-------+------+------+------+------+------+------+
However a basic query for France shows that the RefCount Should be 6 (4 home & 2 away) and this applies to all the results in that they only display home sums.
What am I doing wrong and how can I make this query show the correct result?
Appreciate the help.
Making the aggregates on the outer level:
SELECT Team,
COUNT(RefCount) AS RefCount,
SUM(Tries) AS Tries,
SUM(PT) AS PT,
SUM(Conv) AS Conv,
SUM(PG) AS PG,
SUM(DG) AS DG,
SUM(YC) AS YC,
SUM(RC) AS RC
FROM
(SELECT T1.TeamID AS ID,
T1.Name AS Team,
M.REF AS RefCount,
M.HTries AS Tries,
M.HPT AS PT,
M.HConv AS Conv,
M.HPG AS PG,
M.HDG AS DG,
M.HYC AS YC,
M.HRC AS RC
FROM matches M
LEFT JOIN teams T1 ON T1.TeamID=M.HTeam
WHERE M.Ref = 2
AND M.date < CURDATE()
UNION SELECT T1.TeamID AS ID,
T2.Name AS Team,
M.REF AS RefCount,
M.ATries AS Tries,
M.APT AS PT,
M.AConv AS Conv,
M.APG AS PG,
M.ADG AS DG,
M.AYC AS YC,
M.ARC AS RC
FROM matches M
LEFT JOIN teams T2 ON T2.TeamID=M.ATeam
WHERE M.Ref = 2
AND M.date < CURDATE() )
GROUP BY ID
ORDER BY RefCount DESC,
Team ASC
Note: maybe outer aliases need to be different.
Given the home/away results are the same maybe the Match structure should avoid the distinction and have a ENUM('Home', 'Away') and a Opponent UNSIGNED INT (match ID of opposing game). This would of course require a greater consideration of other queries however if you are early in the process maybe worth considering.
I have a table 'A' that looks something like:
_______________________________________________________________
|query_id | query | response |user_response_count |
|---------------------------------------------------------------
| 1 | acne | BothBad | 2 |
| 1 | acne | BothGood | 1 |
| 2 | asthma | BothBad | 1 |
| 2 | asthma | product 1 | 1 |
| 2 | asthma | BothGood | 1 |
| 3 | bell palsy | product 2 | 2 |
| 3 | bell palsy | BothGood | 1 |
---------------------------------------------------------------
I want to write a query to get something that looks like:
__________________________________________________________________________________
| query_id | query | BothGood | BothBad | Product 1 | Product 2 |
-----------------------------------------------------------------------------------
| 1 | acne | 1 | 2 | 0 | 0 |
| 2 | asthma | 1 | 1 | 1 | 0 |
| 3 | bell palsy| 1 | 0 | 0 | 2 |
-----------------------------------------------------------------------------------
That "user_response_count" column actually says, 2 users selected "BothBad" option for "acne" query.
I know, by using max, I can change my rows to the column, but here it would be difficult to the max. Any Thoughts?
Conditional aggregation:
select query_id, query,
sum(case when response = 'BothGood' then cnt else 0 end) as BothGood,
sum(case when response = 'BothBad' then cnt else 0 end) as BothBad,
sum(case when response = 'product 1' then cnt else 0 end) as product1,
sum(case when response = 'product 2' then cnt else 0 end) as product2
from a
group by query_id, query;
You can use a conditional aggregation as
select query_id, query,
max( coalesce(case when response = 'BothGood' then user_response_count end,0) )
as BothGood,
max( coalesce(case when response = 'BothBad' then user_response_count end,0) )
as BothBad,
max( coalesce(case when response = 'product 1' then user_response_count end,0) )
as Product_1,
max( coalesce(case when response = 'product 2' then user_response_count end,0) )
as Product_2
from tableA
group by query_id, query
Demo
Here is a part of my MySQL Table:
| id | opentrap | openrect | lampgroen | lamprood | closeout | openout | limitswitchclose | limitswitchopen | traploop | photocells | rectloop | DateTime | milliseconds |
| 92 | 0 | 0 | 1 | 1 | 0 | 1 | 0 | 1 | 1 | 1 | 1 | 2018-11-09 13:56:41 | 654 |
| 93 | 0 | 0 | 1 | 1 | 0 | 1 | 0 | 1 | 1 | 1 | 1 | 2018-11-09 13:56:42 | 262 |
| 94 | 0 | 0 | 1 | 1 | 0 | 1 | 0 | 1 | 0 | 0 | 1 | 2018-11-09 13:56:42 | 561 |
| 95 | 0 | 0 | 1 | 1 | 0 | 0 | 0 | 1 | 0 | 0 | 0 | 2018-11-09 13:56:45 | 83 |
| 96 | 0 | 0 | 1 | 1 | 0 | 1 | 0 | 1 | 1 | 0 | 0 | 2018-11-09 13:56:46 | 189 |
| 97 | 0 | 0 | 1 | 1 | 0 | 1 | 0 | 1 | 1 | 1 | 0 | 2018-11-09 13:56:47 | 402 |
| 98 | 0 | 0 | 1 | 1 | 0 | 1 | 0 | 1 | 0 | 1 | 1 | 2018-11-09 13:56:48 | 611 |
| 99 | 0 | 0 | 1 | 1 | 0 | 1 | 0 | 1 | 0 | 0 | 1 | 2018-11-09 13:56:48 | 916 |
I want to select a row based on alot of conditions and based if the next row has
traploop = 1 or rectloop = 1
The query I want to use looks like this (but I can't figure out how to write a condition for the next row):
SELECT *
FROM table
WHERE closeout = 0
AND openout = 0
AND lampgroen = 1
AND lamprood = 1
AND limitswitchopen = 1
AND NEXTROW: traploop = 1
OR rectloop = 1;
This should give me:
| 95 | 0 | 0 | 1 | 1 | 0 | 0 | 0 | 1 | 0 | 0 | 0 | 2018-11-09 13:56:45 | 83 |
Here is one approach, using correlated subqueries:
SELECT *
FROM yourTable t1
WHERE closeout = 0 AND openout = 0 AND lampgroen = 1 AND lamprood = 1 AND
limitswitchopen = 1 AND
((SELECT t2.traploop FROM yourTable t2
WHERE t2.id > t1.id ORDER BY t2.id LIMIT 1) = 1 OR
(SELECT t2.rectloop FROM yourTable t2
WHERE t2.id > t1.id ORDER BY t2.id LIMIT 1) = 1);
Demo
This approach is robust to the possibility that the id values may not be continuous. The only requirement for the "next" row is that it have the next highest id value from the current row.
This is what you need:
SELECT t0.*
FROM
table t0
INNER JOIN
table t1
ON t0.id + 1 = t1.id
WHERE
t0.closeout = 0 AND
t0.openout = 0 AND
t0.lampgroen = 1 AND
t0.lamprood = 1 AND
t0.limitswitchopen = 1 AND
(t1.traploop = 1 OR t1.rectloop = 1);
You can get the "next" row using join -- assuming that "next" is based on id and that has no gaps:
select . . .
from t join
tnext
on tnext.id = t.id + 1
where . . .;
You can then apply the conditions to either t or tnext.
In MySQL 8+, lead() would be a better alternative.
You can try with Correlated Subquery with Exists()
SELECT t1.*
FROM table AS t1
WHERE t1.closeout = 0 AND
t1.openout = 0 AND
t1.lampgroen = 1 AND
t1.lamprood = 1 AND
t1.limitswitchopen = 1 AND
EXISTS (SELECT 1
FROM table AS t2
WHERE t2.id = t1.id + 1 AND
(t2.traploop = 1 OR t2.rectloop = 1)
)
PS: This assumes that there are no gaps in id column
A simple query that uses correlated sub queries but works if ids have gaps is this:
SELECT *
FROM t AS curr
WHERE closeout = 0 AND openout = 0 AND lampgroen = 1 AND lamprood = 1 AND limitswitchopen = 1
AND 1 = (
SELECT CASE WHEN traploop = 1 OR rectloop = 1 THEN 1 END
FROM t AS next
WHERE id > curr.id
ORDER BY id
LIMIT 1
)
Demo on DB Fiddle
I am trying to build an SQL statement (for MySQL) to solve the following problem:
I have a table transport_table with the information from which location there are transports to which other location. This table is ordered by transport_from (ascending), and then by transport_to (ascending). It is possible that some locations only receive good, others only send goods, most locations, however, send and receive goods. It is also possible to send goods to the own location.
---------------------------------
| transport_from | transport_to |
-----------------+---------------
| 1 | 2 |
-----------------+---------------
| 1 | 3 |
-----------------+---------------
| 1 | 7 |
-----------------+---------------
| 3 | 3 |
-----------------+---------------
| 3 | 5 |
-----------------+---------------
| 4 | 5 |
-----------------+---------------
| 4 | 6 |
-----------------+---------------
| 4 | 7 |
-----------------+---------------
| 5 | 3 |
-----------------+---------------
| 7 | 1 |
-----------------+---------------
| 7 | 6 |
---------------------------------
But I need the table in another format, and it would be great if this could be achieved by MySQL (if this is not possible, I could still use a real programming language). Can you help me how to get to a table indicating with boolean values if there was a transport between the two locations. to achieve this, I need to transfer the rows to the columns, and to set the boolean values if a specific row exists in the original table. But I have no idea how to achieve this.
Such a table should look like this:
----------------------------------
| id | 1 | 2 | 3 | 4 | 5 | 6 | 7 |
-----+---+---+---+---+---+---+----
| 1 | 0 | 1 | 1 | 0 | 0 | 0 | 1 |
-----+---+---+---+---+---+---+----
| 2 | 0 | 0 | 0 | 0 | 0 | 0 | 0 |
-----+---+---+---+---+---+---+----
| 3 | 0 | 0 | 1 | 0 | 1 | 0 | 0 |
-----+---+---+---+---+---+---+----
| 4 | 0 | 0 | 0 | 0 | 1 | 1 | 1 |
-----+---+---+---+---+---+---+----
| 5 | 0 | 0 | 1 | 0 | 0 | 0 | 0 |
-----+---+---+---+---+---+---+----
| 6 | 0 | 0 | 0 | 0 | 0 | 0 | 0 |
-----+---+---+---+---+---+---+----
| 7 | 1 | 0 | 0 | 0 | 0 | 1 | 0 |
----------------------------------
It would be great if you helped me with this problem. Thank you very much!
Assuming that rows represent TRANSPORT_FROM and columns represent TRANSPORT_TO(because that seems to be what you meant), try:
SELECT A.TRANSPORT_FROM,
MAX(CASE WHEN TRANSPORT_TO = 1 THEN 1 ELSE 0 END) AS TO_1,
MAX(CASE WHEN TRANSPORT_TO = 2 THEN 1 ELSE 0 END) AS TO_2,
MAX(CASE WHEN TRANSPORT_TO = 3 THEN 1 ELSE 0 END) AS TO_3,
MAX(CASE WHEN TRANSPORT_TO = 4 THEN 1 ELSE 0 END) AS TO_4,
MAX(CASE WHEN TRANSPORT_TO = 5 THEN 1 ELSE 0 END) AS TO_5,
MAX(CASE WHEN TRANSPORT_TO = 6 THEN 1 ELSE 0 END) AS TO_6,
MAX(CASE WHEN TRANSPORT_TO = 7 THEN 1 ELSE 0 END) AS TO_7
FROM
(SELECT DISTINCT TRANSPORT_FROM FROM TRANSPORT_TABLE UNION SELECT DISTINCT TRANSPORT_TO FROM TRANSPORT_TABLE) A
LEFT JOIN
TRANSPORT_TABLE B
ON A.TRANSPORT_FROM = B.TRANSPORT_FROM
GROUP BY A.TRANSPORT_FROM;
I have done a UNION in an inner query to fill for the missing IDs(2 and 6) for the TRANSPORT_FROM. You could also do this by creating a dummy table containing all TRANSPORT_FROM IDs and using that table in place of that inner query represented by the alias A.
EDIT To make it dynamic.
I am not sure if this will work but I have tried a version that should at least give a direction.
SELECT
GROUP_CONCAT(
CONCAT(
' MAX(CASE WHEN TRANSPORT_TO = ',
t.TRANSPORT_TO,
' THEN 1 ELSE 0 END) AS TO_',
t.TRANSPORT_TO
)
) INTO #PivotQuery
FROM
(SELECT TRANSPORT_TO FROM TRANSPORT_TABLE GROUP BY TRANSPORT_TO) t;
SET #PivotQuery = CONCAT('SELECT TRANSPORT_FROM,', #PivotQuery, ' FROM TRANSPORT_TABLE GROUP BY TRANSPORT_FROM ORDER BY TRANSPORT_FROM');
PREPARE statement FROM #PivotQuery;
EXECUTE statement;
DEALLOCATE PREPARE statement;
I have the following table. I would like to add 2 new columns with a select query that will show the total based on the flag type.
Table:
tt | company | count | flag
--------------------------------------------
123 | adeco | 5 | 1
123 | mic | 4 | 2
333 | manpower | 88 | 2
444 | linar | 2 | 2
555 | dlank | 3 | 1
Desired:
tt | company | total | flag | total_flag1 | total_flag2
-------------------------------------------------------------------
123 | adeco | 5 | 1 | 5 | 0
123 | mic | 4 | 2 | 0 | 4
333 | manpower | 88 | 2 | 0 | 88
444 | linar | 2 | 2 | 0 | 2
555 | dlank | 3 | 1 | 3 | 0
By your desired result, you should use case when or if syntax to to this:
select
yourtable.*,
case when flag = 1 then `count` else 0 end as total_flag1,
case when flag = 2 then `count` else 0 end as total_flag2
from yourtable
Or
select
yourtable.*,
if(flag = 1, `count`, 0) as total_flag1,
if(flag = 2, `count`, 0) as total_flag2
from yourtable
I think you can do what you want using correlated subqueries or join:
select t.*, tsum.total_flag1, tsum.total_flag2
from t join
(select t.tt,
sum(case when flag = 1 then total else 0 end) as total_flag1,
sum(case when flag = 2 then total else 0 end) as total_flag2
from t
group by t.tt
) tsum
on t.tt = tsum.tt;