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;
Related
I need a help to create a view in MySQL.
I have a table in the name of competitions like below:
+---------+-----+-----+-----+
|id| name |rank1|rank2|rank3|
+--+------+-----+-----+-----+
| 1| cmpt1| 4 | 3 | 9 |
| 2| cmpt2| 3 | 7 | 8 |
| 3| cmpt3| 4 | 1 | 2 |
| 4| cmpt4| 5 | 8 | 4 |
| 5| cmpt5| 9 | 3 | 2 |
| 6| cmpt6| 1 | 8 | 2 |
+--+------+-----+-----+-----+
the rank1,2,3 values refer to the player id who has such rank at the end of that competition.
Now I want to create a MySQL view to show each player's total medals. Rank 1, 2, and 3 received gold, silver, and bronze medal respectively.
The output of the view will be like following table:
+------+------------+-------------+-------------+
|player| gold_medals|silver_medals|bronze_medals|
+------+------------+-------------+-------------+
| 1 | 4 | 7 | 1 |
| 2 | 7 | 0 | 9 |
| 3 | 1 | 4 | 6 |
| 4 | 0 | 2 | 8 |
| 5 | 2 | 8 | 0 |
| 6 | 3 | 1 | 1 |
+------+------------+-------------+-------------+
Thanks in advance
I assumed you have another table for list players :
select p.playerid
, count(case when playerid = rank1 then 1 end) gold_medals
, count(case when playerid = rank2 then 1 end) silver_medals
, count(case when playerid = rank3 then 1 end) bronze_medals
from
players p
left join ranks r
on p.playerid in (rank1, rank2, rank3)
group by p.playerid
playerid | gold_medals | silver_medals | bronze_medals
-------: | ----------: | ------------: | ------------:
1 | 1 | 1 | 0
2 | 0 | 0 | 3
3 | 1 | 2 | 0
4 | 2 | 0 | 1
5 | 1 | 0 | 0
6 | 0 | 0 | 0
7 | 0 | 1 | 0
8 | 0 | 2 | 1
9 | 1 | 0 | 1
db<>fiddle here
You can unpivot and aggregate:
select playerid,
sum(ranking = 1) as num_gold,
sum(ranking = 2) as num_silver,
sum(ranking = 3) as num_bronze
from ((select rank1 as playerid, 1 as ranking
from ranks
) union all
(select rank2, 2 as ranking
from ranks
) union all
(select rank3, 3 as ranking
from ranks
)
) p
group by playerid;
Note: This only includes players who have a ranking. Your question doesn't include a source of all players, so that seems sufficient.
Here is a db<>fiddle.
Note that older versions of MySQL (pre-5.7 I think) don't support subqueries in the FROM clause of views. Happily that restriction is no longer in force.
Consider the following table:
____________________________________
| my_table |
| ID | val_1 | val_2 | val_3 | val_4 |
| 1 | 1 | 1 | 2 | 0 |
| 2 | 1 | 2 | 8 | 1 |
| 3 | 1 | 2 | 9 | 1 |
| 4 | 1 | 2 | 10 | 1 |
| 5 | 1 | 3 | 6 | 1 |
| 6 | 1 | 4 | 8 | 1 |
| 7 | 2 | 1 | 14 | 1 |
| 8 | 2 | 2 | 1 | 0 |
| 8 | 2 | 2 | 8 | 1 |
| 8 | 2 | 3 | 2 | 0 |
------------------------------------
I need sum(val_3) for every combination of val_1,val_2 where val_4=1, or 0 if there is no val_4=1 for the given val_1,val_2
This query gets the proper sum grouping, but does not include a 0 value for empty combinations:
select val_1,val_2,sum(val_3) from my_table where val_4 = 1 group by val_1,val_2
I can get the proper result with a combination of LEFT JOIN on the same table and IFNULL(<condition>,0)
The first query averages .22 seconds on my dataset. The LEFT JOIN / IFNULL query averages 0.98 seconds. Is there a query structure that will include the 0 values and perform closer to the 0.22s time of the first query? My script may run this query a few thousand times per call.
This is actually a subquery to an outer query that calculates the stdev_samp() of val_3, so I do need to include 0 values.
We can do conditional aggregation without using any Derived Tables (subquery), or Left Join.
Query
SELECT
val_1,
val_2,
SUM(CASE WHEN val_4 = 1 THEN val_3
ELSE 0
END) AS sum
FROM my_table
GROUP BY val_1, val_2;
Result
| val_1 | val_2 | sum |
| ----- | ----- | --- |
| 1 | 1 | 0 |
| 1 | 2 | 27 |
| 1 | 3 | 6 |
| 1 | 4 | 8 |
| 2 | 1 | 14 |
| 2 | 2 | 8 |
| 2 | 3 | 0 |
View on DB Fiddle
Without your obvious data to test and compare to, I offer the following. The inner query goes through the list ONCE and does a sum as both the val_4 = 1 AND when = 0 as respective individual column results with the grouping. Once this is done, all combinations are already resolved with the group by. So now the outer query gets the Val 1 & 2, but a case-when for the final. If there is a value in the SumWhen1 > 0, we know there WAS a value with a 1. If no value, then it returns the sumWhen0 results.
SELECT
pq.Val_1,
pq.Val_2,
CASE when PQ.SumWhen1 > 0 then SumWhen1 else SumWhen0 end FinalSum
from
( select
val_1,
val_2,
sum( CASE when val_4 = 1 then val_2 else 0 end ) SumWhen1,
sum( CASE when val_4 = 0 then val_2 else 0 end ) SumWhen0,
from
my_table
where
val_4 = 1
group by
val_1,
val_2 ) PQ
Now, if your data can contain negatives, I would just add to the inner query the following and use this column as the basis to confirm a val_4 had any records.
sum( CASE when val_4 = 1 then 1 else 0 end ) Val1Records,
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;
I have 2 tables bellow
0 --> Pending
1 --> Success
2 --> Fail
table : mntnc
+-------+-------+-------+
| id | own | sts |
+-------+-------+-------+
| 1 | BN | 1 |
| 2 | BB | 2 |
| 3 | BN | 1 |
| 4 | BD | 1 |
| 5 | BD | 0 |
table : istlsi
+-------+-------+-------+
| id | own | sts |
+-------+-------+-------+
| 1 | BN | 1 |
| 2 | BB | 1 |
| 3 | BB | 1 |
| 4 | BC | 0 |
| 5 | BD | 2 |
of the two tables above, I want to add both of them to be the table below
+-------+-----------+-----------+-----------+
| own | success | fail | pending |
+-------+-----------+-----------+-----------+
| BN | 3 | 0 | 0 |
| BB | 2 | 1 | 0 |
| BD | 1 | 1 | 1 |
| BC | 0 | 0 | 1 |
The two key points here:
Union tables (I aliased result to B)
Use sum(case...) for each column.
First we union both tables together as an inline view.
We then use a case statement for each desired column and evaluate the status setting the value to 1 or 0 depending on sts value. and then sum those...
SELECT own
, sum(case when sts=1 then 1 else 0 end) as Success
, sum(case when sts=2 then 1 else 0 end) as Fail
, sum(case when sts=0 then 1 else 0 end) as Pending
FROM ( SELECT ID, own, sts
FROM mntnc
UNION ALL
SELECT id, own, sts
FROM istlsi
) B
GROUP BY own
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