make 2-dimension coordinate using mysql select results - mysql

I have question about mysql queries.
I have a table which have data below.
From To Weight
--------------
A B 1
A C 3
B C 2
D E 4
And I want to get sql result like below..
(?) A B C D E
----------------------
A 0 1 3 0 0
B 0 0 2 0 0
C 0 0 0 0 0
D 0 0 0 0 4
E 0 0 0 0 0
And what data is in original table is not determined.
How can I acheive this?

If you know the original columns, you can do:
select c.col1,
sum(case when to = 'A' then weight else 0 end) as a,
sum(case when to = 'B' then weight else 0 end) as b,
sum(case when to = 'C' then weight else 0 end) as c,
sum(case when to = 'D' then weight else 0 end) as d,
sum(case when to = 'E' then weight else 0 end) as d
from (select 'A' as col1 union all select 'B' union all select 'C' union all select 'D' union all select 'E'
) c left join
t
on t.from = c.col1
group by c.col1;
If you don't know the original columns, you could combine the values into a single string:
select col1.col,
group_concat(col2.col, ':', t.weight order by col2.col)
from ((select `from` as col from t
) union -- on purpose to remove duplicates
(select `to` from t
)
) col1 cross join
((select `from` as col from t
) union -- on purpose to remove duplicates
(select `to` from t
)
) col2 left join
t
on col1.col = t.`from` and col2.col = t.`from`
group by col1.col;
If you actually want separate columns and don't know the values, then you would need dynamic SQL.

Related

how to get column count

i have a query like below
select project_task_id,
status_id,
sum(case when StatusID=1 then 1 else 0 end) as task_id=1,
sum(case whenStatusID=2 then 1 else 0 end) as task_id=2,
sum(case when StatusID=3 then 1 else 0 end) as task_id=3,
sum(case when StatusID=4 then 1 else 0 end) as task_id=4,
sum(case when StatusID=5 then 1 else 0 end) as task_id=5,
sum(case when StatusID=6 then 1 else 0 end) as task_id=6,
sum(case when StatusID=7 then 1 else 0 end) as task_id=7,
from"Projects".work_unit_status
group by project_task_id,status_id;
I'm getting the below attached output:
https://i.stack.imgur.com/1wfD1.png
and I want to get the below expected output:
https://i.stack.imgur.com/Zql9z.png
include zero's if the status_id is blank
please any one help on this
Try this: use the addition of all sum column
select project_task_id,status_id,
isnull(sum(case when StatusID=1 then 1 else 0 end),0)+
isnull(sum(case whenStatusID=2 then 1 else 0 end),0) +
isnullsum(case when StatusID=3 then 1 else 0 end),0) +
isnullsum(case when StatusID=4 then 1 else 0 end),0)+
isnullsum(case when StatusID=5 then 1 else 0 end),0) +
isnullsum(case when StatusID=6 then 1 else 0 end),0) +
isnullsum(case when StatusID=7 then 1 else 0 end),0) as count_status
from"Projects".work_unit_status group by project_task_id,status_id
use in with your case
with t1 as (
select project_task_id,
status_id,
sum(case when StatusID in (1,2,3,4,5,6,7) then 1 else 0)
as sum_s
from "Projects".work_unit_status
group by project_task_id,status_id
) ,
t2 as
(
select * from (
select 1 as statusid
union
select 2
union
select 3
union
select 4
union
select 5
union
select 6
union
select 7 ) t
) select t1.project_task_id,
t2.statusid,
case when t1.sum_s>0 or not null
then sum_s else 0 end as total
t2 full join t1 on t2.statusid=t1.status_id
Without knowing the exact table structure I assumed that status_id and statusId refer to the same column. (If they are different columns, we need to use StatusId in the COUNT.)
Based on the expected output, you want to count the status_id and group by project_task_id. To make sure that every status is represented for every task, first, we need to create a subquery of all possible project_task_id/status_id combinations. Then we use that with the aggregate values of the original table.
select
ps.project_task_id,
ps.status_id,
count(w.status_id) as total
from (
select distinct
project_task_id,
s.status_id
from work_unit_status
cross join (select distinct status_id from work_unit_status) s
) ps
left join work_unit_status w
on ps.project_task_id = w.project_task_id and ps.status_id = w.status_id
group by
ps.project_task_id,
ps.status_id
If you really need to hardcode the statuses from 1 to 7, use the query below.
select
ps.project_task_id,
ps.status_id,
count(w.status_id) as total
from (
select distinct
project_task_id,
s.status_id
from work_unit_status
cross join (
select 1 as status_id
union select 2
union select 3
union select 4
union select 5
union select 6
union select 7
) s
) ps
left join work_unit_status w
on ps.project_task_id = w.project_task_id and ps.status_id = w.status_id
group by
ps.project_task_id,
ps.status_id
order by
ps.project_task_id,
ps.status_id

SQL-Print column name based on value and condition

I have been trying to print column names based on condition and value.
My problem is this
If two all the columns in the two rows like for data x and y have value yes the it should print those column names otherwise not
My code:
select 'A' from world.city where A = 'yes' AND data=y and data=x union all
select 'B' from world.city where B= 'yes' AND data=y and data=x union all
select 'C' from world.city where C= 'yes' AND data=y and data=x union all
select 'D' from world.city where D= 'yes' AND data=y and data=x union all
select 'E' from world.city where E= 'yes' AND data=y and data=x;
It is not giving perfect results.
Do a sum and union all then filter the rows with sum('yes') is equal to 2. See below.
select colname
from (
select 'A' as colname,sum(case when A='yes' then 1 else 0 end) col
from tbl where data in ('X','Y')
union all
select 'B',sum(case when B='yes' then 1 else 0 end)
from tbl where data in ('X','Y')
union all
select 'C',sum(case when C='yes' then 1 else 0 end)
from tbl where data in ('X','Y')
union all
select 'D',sum(case when D='yes' then 1 else 0 end)
from tbl where data in ('X','Y')
union all
select 'E',sum(case when E='yes' then 1 else 0 end)
from tbl where data in ('X','Y')) tab
where col = 2;
Result:
colname
B
D

How to Group and display count of each type of data?

Suppose I have a column which has three types of data like A, B, C. I want to group and count the number of each type.
For example if A is 3 times in column , B is 2 times and C is 1 time.
It should display as:
A B C
3 2 1
I would appreciate your help. Thankyou.
If you want the data in one row, you can use conditional aggregation:
select sum(col = 'a') as A, sum(col = 'b') as b, sum(col = 'c') as c
from t;
You can try the more explicit:
select sum(case when col = 'a' then 1 else 0 end) as A,
sum(case when col = 'b' then 1 else 0 end) as b,
sum(case when col = 'c' then 1 else 0 end) as c
from t;
I would actually suggest the following query:
SELECT
data, COUNT(*) AS cnt
FROM yourTable
GROUP BY data
This would generate counts in row form, e.g.
A 3
B 2
C 1
Most of the time this would meet your needs. If you instead really need columns you can try this:
SELECT
SUM(CASE WHEN data = 'A' THEN 1 ELSE 0 END) AS A,
SUM(CASE WHEN data = 'B' THEN 1 ELSE 0 END) AS B,
SUM(CASE WHEN data = 'C' THEN 1 ELSE 0 END) AS C
FROM yourTable
This would generate your literal expected output:
A B C
3 2 1

Mysql split row sum by count

I have the following tables
http://sqlfiddle.com/#!2/d0a3d
sp | product | exp
1 A | 50
1 B 50
1 A 100
1 B 100
2 B 200
2 C 200
3 A 50
3 B 50
Technical I want to divide exp to total number of products associated with t_id
The final result should be
sp | A | B | C
1 | 150 | 150 | 0
2 | 0 | 200 | 200
3 | 50 | 50 | 0
Can be done like this (done to match your sql fiddle columns):-
SELECT b.sp_id,
SUM(IF(a.product = 'A', b.exp, 0)) AS A,
SUM(IF(a.product = 'B', b.exp, 0)) AS B,
SUM(IF(a.product = 'C', b.exp, 0)) AS C
FROM topic_product a
INNER JOIN exp_speaker_topic b
ON a.t_id = b.t_id
GROUP BY b.sp_id
But a mess waiting to happen when extra values get added.
EDIT - amended to give what I think you are saying you want.
SELECT sp_id,
SUM(IF(product = 'A', avg_exp, 0)) AS A,
SUM(IF(product = 'B', avg_exp, 0)) AS B,
SUM(IF(product = 'C', avg_exp, 0)) AS C
FROM
(
SELECT sp_id, a.product, exp / Sub1.product_count AS avg_exp
FROM topic_product a
INNER JOIN exp_speaker_topic b
ON a.t_id = b.t_id
INNER JOIN
(
SELECT t_id, COUNT(*) AS product_count
FROM topic_product
GROUP BY t_id
) Sub1
ON a.t_id = Sub1.t_id
) Sub2
GROUP BY sp_id
SQL fiddle:-
http://sqlfiddle.com/#!2/d0a3d/33
OK, slow day. Just do the following, and handle missing results and display logic in the presentation layer/application-level code (e.g. a simple php loop acting upon an ordered array)...
SELECT p.product
, s.sp_id
, SUM(s.exp/x.cnt) total
FROM topic_product p
JOIN exp_speaker_topic s
ON s.t_id = p.t_id
JOIN
( SELECT t_id
, COUNT(0) cnt
FROM topic_product
GROUP
BY t_id
) x
ON x.t_id = p.t_id
GROUP
BY sp_id,product;
Here is the SQL Fiddle demonstrating the below queries.
You could also use CASE statements like the following:
SELECT e.sp_id,
SUM(CASE WHEN t.product = 'A' THEN e.exp ELSE 0 END) AS A,
SUM(CASE WHEN t.product = 'B' THEN e.exp ELSE 0 END) AS B,
SUM(CASE WHEN t.product = 'C' THEN e.exp ELSE 0 END) AS C
FROM topic_product t INNER JOIN exp_speaker_topic e ON t.t_id = e.t_id
GROUP BY e.sp_id;
If you want to divide by the number of record use the following:
SELECT e.sp_id,
SUM(CASE WHEN t.product = 'A' THEN e.exp ELSE 0 END) /
SUM(CASE WHEN t.product = 'A' THEN 1 ELSE 0 END) AS A,
SUM(CASE WHEN t.product = 'B' THEN e.exp ELSE 0 END) /
SUM(CASE WHEN t.product = 'B' THEN 1 ELSE 0 END) AS B,
SUM(CASE WHEN t.product = 'C' THEN e.exp ELSE 0 END) /
SUM(CASE WHEN t.product = 'C' THEN 1 ELSE 0 END) AS C
FROM topic_product t INNER JOIN exp_speaker_topic e ON t.t_id = e.t_id
GROUP BY e.sp_id;
If you want to get rid of the Nulls you could use the following:
SELECT m.sp_id,
CASE WHEN ISNULL(m.A) = 0 THEN m.A ELSE 0 END AS A,
CASE WHEN ISNULL(m.B) = 0 THEN m.B ELSE 0 END AS B,
CASE WHEN ISNULL(m.C) = 0 THEN m.C ELSE 0 END AS C
FROM
(
SELECT e.sp_id,
SUM(CASE WHEN t.product = 'A' THEN e.exp ELSE 0 END) / SUM(CASE WHEN t.product = 'A' THEN 1 ELSE 0 END) AS A,
SUM(CASE WHEN t.product = 'B' THEN e.exp ELSE 0 END) / SUM(CASE WHEN t.product = 'B' THEN 1 ELSE 0 END) AS B,
SUM(CASE WHEN t.product = 'C' THEN e.exp ELSE 0 END) / SUM(CASE WHEN t.product = 'C' THEN 1 ELSE 0 END) AS C
FROM topic_product t INNER JOIN exp_speaker_topic e ON t.t_id = e.t_id
GROUP BY e.sp_id
) AS m;

Count data from multiple tables using SUM

I have table with positions
tbl_positions
id position
1 Driver
2 Lobby
3 Support
4 Constructor
and in other table i have users
EDIT:
tbl_workers
id name position position2 status
1 John 2 3 4
2 Mike 3 2 2
3 Kate 2 0 3
4 Andy 1 0 0
i do request of positions
SELECT p.id,
p.position,
SUM(CASE w.Status WHEN 2 THEN 1 ELSE 0 END) AS booked,
SUM(CASE w.Status WHEN 3 THEN 1 ELSE 0 END) AS placed
FROM tbl_positions AS p LEFT JOIN tbl_workers AS w
ON w.position=p.id
GROUP BY p.id, p.position
I need output like this in single query.
Position booked placed
Driver 0 0
Lobby 1 2
Support 0 2
Constructor 0 0
I need to evalate both field positon1 and position2 instead of one. I think its easy to modify it but i cannot find the right solution please help.
EDIT : Added status 4
Instead of joining to tbl_workers you could join to its unpivoted variation where position and position2 would be in the same column but in different rows.
Here's how the unpivoting might look like:
SELECT
w.id,
w.name,
CASE x.pos WHEN 1 THEN w.position ELSE w.position2 END AS position,
w.status
FROM tbl_workers AS w
CROSS JOIN (SELECT 1 AS pos UNION ALL SELECT 2) AS x
Here's the entire query, which is basically your original query with the above query substituting for the tbl_workers table:
SELECT p.id,
p.position,
SUM(CASE w.Status WHEN 2 THEN 1 ELSE 0 END) AS booked,
SUM(CASE w.Status WHEN 3 THEN 1 ELSE 0 END) AS placed
FROM tbl_positions AS p
LEFT JOIN (
SELECT
w.id,
w.name,
CASE x.pos WHEN 1 THEN w.position ELSE w.position2 END AS position,
w.status
FROM tbl_workers AS w
CROSS JOIN (SELECT 1 AS pos UNION ALL SELECT 2) AS x
) AS w
ON w.position=p.id
GROUP BY p.id, p.position
UPDATE
This is a modified script according to additional request in comments:
SELECT p.id,
p.position,
SUM(CASE w.Status WHEN 2 THEN 1 ELSE 0 END) AS booked,
SUM(CASE w.Status WHEN 3 THEN 1 ELSE 0 END) AS placed
FROM tbl_positions AS p
LEFT JOIN (
SELECT
w.id,
w.name,
CASE x.pos WHEN 1 THEN w.position ELSE w.position2 END AS position,
CASE w.status
WHEN 4 THEN CASE x.pos WHEN 1 THEN 3 ELSE 2 END
ELSE w.status
END AS status
FROM tbl_workers AS w
CROSS JOIN (SELECT 1 AS pos UNION ALL SELECT 2) AS x
) AS w
ON w.position=p.id
GROUP BY p.id, p.position
The idea is to substitute the 4 status in the subselect with 3 or 2 depending on whether we are currently to pull position or position2 as the unified position. The outer select keeps using the same logic as before.
not really sure what you mean by "evaluate both fields" - but here is an example on how to sum up both:
SELECT
P.ID,
P.POSITION,
((SELECT SUM (CASE W.STATUS WHEN 2 THEN 1 ELSE 0 END) FROM TBL_WORKERS W WHERE W.POSITION = P.ID) + (SELECT SUM (CASE W.STATUS WHEN 2 THEN 1 ELSE 0 END) FROM TBL_WORKERS W WHERE W.POSITION2 = P.ID)) AS BOOKED,
((SELECT SUM (CASE W.STATUS WHEN 3 THEN 1 ELSE 0 END) FROM TBL_WORKERS W WHERE W.POSITION = P.ID) + (SELECT SUM (CASE W.STATUS WHEN 3 THEN 1 ELSE 0 END) FROM TBL_WORKERS W WHERE W.POSITION2 = P.ID)) AS PLACED
FROM TBL_POSITIONS P
since your data is not normalized (ie: two columns both representing a position in a single row), I would do a UNION of the workers first, then apply your outer count/group by... something like... Each part of the inner query is pre-aggregating and only counting for the records qualifying the status 2 or 3 and have a position. So, if you have a large table, it would be more noticeable for performance. Then, that result gets summed at the outer level for all possible positions using COALESCE() to prevent NULL values in your output result.
SELECT
p.id,
p.position,
coalesce( SUM(w.Booked), 0 ) AS booked,
coalesce( SUM(w.Placed), 0 ) AS placed
FROM
tbl_positions AS p
LEFT JOIN
( select w1.Position as PositionID,
sum( if( w1.status = 2, 1, 0 )) as Booked,
sum( if( w1.status = 3, 1, 0 )) as Placed
from
tbl_workers w1
where
w1.status in ( 2, 3 )
and w1.Position > 0
group by
PositionID
union all
select w2.Position2 as PositionID,
sum( if( w2.status = 2, 1, 0 )) as Booked,
sum( if( w2.status = 3, 1, 0 )) as Placed
from
tbl_workers w2
where
w2.status in ( 2, 3 )
and w2.Position2 > 0
group by
PositionID ) as w
on p.id = w.positionID
group by
p.id,
p.position