Appending data from different tables - mysql - mysql

I have the following tables Job description and Candidate.
Each job description can be related to n candidates. The candidates are obtained from different sources(a, b, c d) - candidate.source.
I want a single query to list me the JD ids, with count of candidates for each source for each JD, as shown below:
JD id | candidate name | count of candidates - source a | count of candidates - source b | count of candidates - source | c..............

Try using the query as a template:
select
JobDescriptionName,
SUM(ACount) CountOfCandidatesOfA ,
SUM(BCount) CountOfCandidatesOfB,
SUM(CCount) CountOfCandidatesOfC ,
SUM(DCount) CountOfCandidatesOfD
from
( select JobDescriptionID, (CASE WHEN Source = 'a' THEN 1 ELSE 0 END) AS ACount,
(CASE WHEN Source = 'b' THEN 1 ELSE 0 END) AS BCount,
(CASE WHEN Source = 'c' THEN 1 ELSE 0 END) AS CCount,
(CASE WHEN Source = 'd' THEN 1 ELSE 0 END) AS DCount
from Candidate) AS DerivedCandidate
inner join JobDescription ON JobDescription.JobDescriptionID = DerivedCandidate.JobDescriptionID group by JobDescriptionID;

I know there is already an accepted answer but in the effort of learning myself more about SQL here is an alternative way applying the case directly in the initial select without the sub-select:
Select
jd.id,
jd.name,
sum(case when c.source = 'a' then 1 else 0 end) as sourceA,
sum(case when c.source = 'b' then 1 else 0 end) as sourceB,
sum(case when c.source = 'c' then 1 else 0 end) as sourceC,
sum(case when c.source = 'd' then 1 else 0 end) as sourceD
from JobDescription as jd
join Candidate as c on c.jobId = jd.id
group by jd.id, jd.name
SQL Fiddle Demo

SELECT JD id, COUNT(Candidate), source
FROM table1
GROUP BY JD id,source

Related

Distinct Count Query

I have a table below:
Item Status1 Status2
-----------------------------
A Good NULL
A Good NULL
A Good NULL
A Bad Good
B Bad Good
B Good NULL
C Good NULL
C Good NULL
C Good NULL
D Bad Good
Now, I'm thinking off writing a query which gives me the result below:
Item Good Bad
-----------------------------
A 4 1
B 2 1
C 3 0
D 1 1
Distinct in the Item column and the count of Good and Bad for each Item where NULL is not counted.
The column name can be of anything (I just kept it as Good and Bad in my second table).
Any suggestions/ideas on how to achieve my desired results?
Use UNION ALL & do aggregation :
select item, sum(status = 'good'), sum(status = 'bad')
from (select item, status1 as status
from table t
union all
select item, status2
from table t
) t
group by item;
You can use union all and conditional aggregation
select item, count(case when status1='good' then 1 end) as good,
count(case when status1='bad' then 1 end) as bad
from
(
select item , status1 from tablename
union all
select item , status2 from tablename
)A group by item
use union and case when
select Item, sum(case when status = 'good' then 1 else 0 end) as good,
sum ( case when status = 'bad' then 1 else 0 end) as bad
from (select Item, Status1 as status
from table_name
union all
select Item, Status2
from table_name
) t
group by Item;
There's no need for UNION, simply apply some logic.
select Item
,sum(case when Status1 = 'Good' then 1 else 0 end +
case when Status2 = 'Good' then 1 else 0 end) as good
,sum(case when Status1 = 'Bad' then 1 else 0 end +
case when Status2 = 'Bad' then 1 else 0 end) as bad
from tab
group by Item
or
select Item
,count(case when Status1 = 'Good' then 1 end) +
count(case when Status2 = 'Good' then 1 end) as good
,count(case when Status1 = 'Bad' then 1 end) +
count(case when Status2 = 'Bad' then 1 end) as good
from tab
group by Item
You can use sub-query and then apply sum function in outer query
select distinct(item) as item, sum(S1G+S2G) as Good,sum(S1B+S2B) as Bad from ( select item, CASE WHEN status1 ='Good' THEN 1 ELSE 0 END as S1G, CASE WHEN status2 ='Good' THEN 1 ELSE 0 END as S2G, CASE WHEN status2 ='Bad' THEN 1 ELSE 0 END as S2B, CASE WHEN status1 ='Bad' THEN 1 ELSE 0 END as S1B from t1 ) as b group by item
Here is demo

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

SQL Vertical grouping

I have the following RDB Table:
ID Feature
1 1
1 2
2 1
3 1
3 2
3 3
What I want is the following output:
ID Feature1 Feature2 Feature3
1 true true false
2 true false false
3 true true true
What's the simplest SQL query to achieve this?
Get all the id's with all feature combinations using a cross join and left join the original table on to this to get the required result.
select i.id,
max(case when f.feature=1 and t.feature is not null then 'true' else 'false' end) as feature1,
max(case when f.feature=2 and t.feature is not null then 'true' else 'false' end) as feature2,
max(case when f.feature=3 and t.feature is not null then 'true' else 'false' end) as feature3
from (select distinct feature from t) f --replace this with the feature table if you have one
cross join (select distinct id from t) i
left join t on t.id=i.id and t.feature=f.feature
group by i.id
If you just need boolean values 1,0 for True,False the query can be simplified to
select i.id,
max(f.feature=1 and t.feature is not null) as feature1,
max(f.feature=2 and t.feature is not null) as feature2,
max(f.feature=3 and t.feature is not null) as feature3
from (select distinct feature from t) f --replace this with the feature table if you have one
cross join (select distinct id from t) i
left join t on t.id=i.id and t.feature=f.feature
group by i.id
I believe all you would need is a simple pivot query. Here, I used boolean values to flag whether or not a feature exists (1 for true, 0 for false).
SELECT f.ID
, Feature1 = SUM(CASE WHEN f.Feature = 1 THEN 1 ELSE 0 END)
, Feature2 = SUM(CASE WHEN f.Feature = 2 THEN 1 ELSE 0 END)
, Feature2 = SUM(CASE WHEN f.Feature = 3 THEN 1 ELSE 0 END)
FROM dbo.Features (NOLOCK) f
GROUP BY f.ID
You just need conditional aggregation:
select id, max(feature = 1) as feature1, max(feature = 2) as feature2,
max(feature = 3) as feature3
from t
group by id;
The above returns 0 and 1. If you actually want the strings true and false, you can do:
select id,
(case when max(feature = 1) then 'true' else 'false' end) as feature1,
(case when max(feature = 2) then 'true' else 'false' end) as feature2,
(case when max(feature = 3) then 'true' else 'false' end) as feature3
from t
group by id;

joining with a group by and pivot

I have two tables as below
tbl1
id qNum
1 1
2 2
3 3
tbl2
id qNum displayNum
1 1 3
2 2 1
3 2 2
4 2 4
Ideally I need a sql results to look like this
qNum display1 display2 display3 display4
1 0 0 1 0
2 1 1 0 1
3 0 0 0 0
I have tried the following sql but this was not correct
SELECT
tbl1.qNum,
CASE when tbl2.displayNum=1 then 1 else 0 end AS filter1,
CASE when tbl2.displayNum=2 then 1 else 0 end AS filter2,
CASE when tbl2.displayNum=3 then 1 else 0 end AS filter3,
CASE when tbl2.displayNum=4 then 1 else 0 end AS filter4,
CASE when tbl2.displayNum=5 then 1 else 0 end AS filter5
FROM
tbl1
Left Join tbl2 ON tbl1.qNum = tbl2.qNum
GROUP BY
tbl1.qNum
Could anyone help a little please!!
You have use MAX function to pivot the table
Try this:
SELECT tbl1.qNum,
MAX(CASE WHEN tbl2.displayNum=1 THEN 1 ELSE 0 END) AS filter1,
MAX(CASE WHEN tbl2.displayNum=2 THEN 1 ELSE 0 END) AS filter2,
MAX(CASE WHEN tbl2.displayNum=3 THEN 1 ELSE 0 END) AS filter3,
MAX(CASE WHEN tbl2.displayNum=4 THEN 1 ELSE 0 END) AS filter4,
MAX(CASE WHEN tbl2.displayNum=5 THEN 1 ELSE 0 END) AS filter5
FROM tbl1
LEFT JOIN tbl2 ON tbl1.qNum = tbl2.qNum
GROUP BY tbl1.qNum
Your query is almost correct, you're just missing an aggregate function:
SELECT
tbl1.qNum,
MAX(CASE when tbl2.displayNum=1 then 1 else 0 end) AS filter1,
MAX(CASE when tbl2.displayNum=2 then 1 else 0 end) AS filter2,
MAX(CASE when tbl2.displayNum=3 then 1 else 0 end) AS filter3,
MAX(CASE when tbl2.displayNum=4 then 1 else 0 end) AS filter4,
MAX(CASE when tbl2.displayNum=5 then 1 else 0 end) AS filter5
FROM
tbl1
Left Join tbl2 ON tbl1.qNum = tbl2.qNum
GROUP BY
tbl1.qNum
The columns you select should always be either in the group by clause or an aggregate function should be applied to them. A group by "collapses" a group of rows and if you don't have an aggregate function on a column (which is not in the group by) a random row of that group is displayed.
Here you can read about the different aggregate functions: GROUP BY (Aggregate) Functions
The MAX() function in our case here returns the greatest value (note: not the row with the greatest value. You can also have a query like this: select min(col), max(col) from whatever).
I just want to point out that in MySQL, you can simplify the expression. It doesn't need a case statement because booleans are treated as integers with values of 0 and 1:
SELECT tbl1.qNum,
MAX(tbl2.displayNum = 1) AS filter1,
MAX(tbl2.displayNum = 2) AS filter2,
MAX(tbl2.displayNum = 3) AS filter3,
MAX(tbl2.displayNum = 4) AS filter4,
MAX(tbl2.displayNum = 5) AS filter5
FROM tbl1 Left Join
tbl2
ON tbl1.qNum = tbl2.qNum
GROUP BY tbl1.qNum;
Normally, I prefer going with ANSI standard syntax. I do, however, find this easier to read than the case syntax.
Also, you may not need tbl1 for the query, if all values you are interested in are already in tbl2.

MySql: Displaying Sums of all rows for each column as an extra row at the end

How do I get the sum of each column for all rows of a result-set? I mean I want to display the sums-row as the last row of my result-set.
My query looks like this:
select f.filename,
count(distinct case when v.rUser like '%bike%' then 1 else null end) as bikeUser,
count(distinct case when v.rUser like '%Pedestrian%' then 1 else null end) as pedestrianUser,
count(distinct case when d.weather like '%clear%' then 1 else null end) as clearWeather,
count(case when m.extras like '%hat%' then 1 else null end) as hatExtras
from VMdata v
inner join files f on v.id = f.id
inner join DMdata d on f.id = d.id
inner join MultiFiledata m on f.id = m.id
where f.filename in (X,Y,Z) group by f.filename;
When I use with roll up after the 'group by' clause, it gives me sum of the groups generated by the 'group by clause' (horizontal sum) whereas I need the vertical sum of each column at the end.
Any ideas?
Your ROLLUP is already correct, it's really the COUNT(CASE WHEN Field = 'Blah' THEN 1 ELSE 0 END) is the main culprit why your query is not working
You must do either:
COUNT(CASE WHEN Field = 'Blah' THEN 1 END)
or
SUM(CASE WHEN Field = 'Blah' THEN 1 ELSE 0 END)
But since MySQL has a duality between boolean and integer type like it is in C language, hence you can do this too:
SUM(Field = 'Blah')
This is your incorrect query (wrong results): http://www.sqlfiddle.com/#!2/70187/1
create table ProductInventory(
ProductCode varchar(10) not null,
Location varchar(50) not null
);
insert into ProductInventory(ProductCode,Location) values('CPU','US');
insert into ProductInventory(ProductCode,Location) values('CPU','US');
insert into ProductInventory(ProductCode,Location) values('CPU','CN');
insert into ProductInventory(ProductCode,Location) values('KB','CN');
insert into ProductInventory(ProductCode,Location) values('KB','JP');
insert into ProductInventory(ProductCode,Location) values('KB','US');
insert into ProductInventory(ProductCode,Location) values('MOUSE','US');
insert into ProductInventory(ProductCode,Location) values('MOUSE','CN');
Incorrect Output:
PRODUCTCODE USQTY CHINAQTY
CPU 3 3
KB 3 3
MOUSE 2 2
8 8
This is the correct query: http://www.sqlfiddle.com/#!2/70187/2
select ProductCode,
COUNT(CASE WHEN Location = 'US' THEN 1 END) as UsQty,
COUNT(CASE WHEN Location = 'CN' THEN 1 END) as ChinaQty
from ProductInventory
group by ProductCode with rollup
Correct output:
PRODUCTCODE USQTY CHINAQTY
CPU 2 1
KB 1 1
MOUSE 1 1
4 3
Please, don't insist that this is correct, this is very incorrect:
COUNT(CASE WHEN Location = 'US' THEN 1 ELSE 0 END) AS UsQty
You must either do this (correct) :
COUNT(CASE WHEN Location = 'US' THEN 1 END) AS UsQty
Or this (correct) : http://www.sqlfiddle.com/#!2/70187/5
SUM(CASE WHEN Location = 'US' THEN 1 ELSE 0 END) AS UsQty
Or this (correct) : http://www.sqlfiddle.com/#!2/70187/6
SUM(CASE WHEN Location = 'US' THEN 1 END) AS UsQty
Or try take advantage of the fact that MySql has duality between boolean and integer (correct) : http://www.sqlfiddle.com/#!2/70187/4
SUM(Location = 'US') AS UsQty
Bottomline
Please don't use this (incorrect) : http://www.sqlfiddle.com/#!2/70187/3
COUNT(Location = 'US') as UsQty
And please don't use this too (incorrect, similar to your query): http://www.sqlfiddle.com/#!2/70187/1
COUNT(CASE WHEN Location = 'US' THEN 1 ELSE 0 END) as UsQty
By the way, this also works, it's for you to find out why ;-)
COUNT(CASE WHEN Location = 'US' THEN 1976 END) AS UsQty
UPDATE
I have an inkling that this is what you need:
create table Product
(
ProductCode varchar(10) not null primary key,
ProductName varchar(100) not null
);
insert into Product(ProductCode,ProductName) values
('CPU','Central Processing Unit'),
('KB','Keyboard'),
('MSE','Mouse'),
('RAM', 'Random Access Memory');
create table ProductInventory(
ProductCode varchar(10) not null,
Location varchar(50) not null
);
insert into ProductInventory(ProductCode,Location) values
('CPU','US'),
('CPU','PH'),
('CPU','PH'),
('KB','PH'),
('KB','US'),
('KB','US'),
('MSE','US'),
('MSE','JP');
select p.ProductCode,
coalesce(SUM(i.Location = 'US'),0) as UsQty,
coalesce(SUM(i.Location = 'PH'),0) as PhilippinesQty
from Product p
left join ProductInventory i on i.ProductCode = p.ProductCode
group by p.ProductCode with rollup
Output:
ProductCode UsQty PhilippinesQty
CPU 1 2
KB 2 1
MSE 1 0
RAM 0 0
4 4
Live test: http://www.sqlfiddle.com/#!2/2bb09/1
Don't use COUNT for counting when you are using evaluation; despite the name, SUM would yield the correct result for counting based on conditions:
Given this: http://www.sqlfiddle.com/#!2/79375/1
create table ProductInventory(
ProductCode varchar(10) not null,
Location varchar(50) not null
);
insert into ProductInventory(ProductCode,Location) values('CPU','US');
insert into ProductInventory(ProductCode,Location) values('CPU','US');
insert into ProductInventory(ProductCode,Location) values('CPU','ARM');
insert into ProductInventory(ProductCode,Location) values('KB','CN');
insert into ProductInventory(ProductCode,Location) values('KB','PH');
insert into ProductInventory(ProductCode,Location) values('KB','US');
insert into ProductInventory(ProductCode,Location) values('MOUSE','AA');
insert into ProductInventory(ProductCode,Location) values('MOUSE','BB');
select ProductCode, COUNT(CASE WHEN Location = 'US' THEN 1 ELSE 0 END) as Qty
from ProductInventory
group by ProductCode
order by ProductCode
That will produce incorrect results:
PRODUCTCODE QTY
CPU 3
KB 3
MOUSE 2
Use SUM instead, correct results: http://www.sqlfiddle.com/#!2/79375/3
select ProductCode, SUM(Location = 'US') as Qty
from ProductInventory
group by ProductCode
order by ProductCode
That would result to:
PRODUCTCODE QTY
CPU 2
KB 1
MOUSE 0
COUNT works by counting the NON-nullness of value or expression
If you still want to use COUNT, pass any non-null value to COUNT; and don't use ELSE NULL END, your query will look tedious, just saying :-) http://www.sqlfiddle.com/#!2/79375/4
select ProductCode, COUNT(CASE WHEN Location = 'US' THEN Location END) as Qty
from ProductInventory
group by ProductCode
order by ProductCode
Output:
PRODUCTCODE QTY
CPU 2
KB 1
MOUSE 0
You can create a temptable (#mytemptable) and add the sum as last entry and then return the select of the created temptable. But this is only working in stored procedures I think.
select f.filename,
count(case when v.rUser like '%bike%' then 1 else 0 end) as bikeUser,
count(case when v.rUser like '%Pedestrian%' then 1 else 0 end) as pedestrianUser,
count(case when d.weather like '%clear%' then 1 else 0 end) as clearWeather,
count(case when m.extras like '%hat%' then 1 else 0 end) as hatExtras
INTO #myTempTable from VMdata v
inner join files f on v.id = f.id
inner join DMdata d on f.id = d.id
inner join MultiFiledata m on f.id = m.id
where f.filename in (X,Y,Z) group by f.filename;
INSERT INTO #myTempTable select(create the sum here but care that you have the same columns)
SELECT * FROM #myTempTable
within just one select you can do it with the command "UNION"
*EDITED: 27.04. 11:55
select f.filename,
count(case when v.rUser like '%bike%' then 1 else 0 end) as bikeUser,
count(case when v.rUser like '%Pedestrian%' then 1 else 0 end) as pedestrianUser,
count(case when d.weather like '%clear%' then 1 else 0 end) as clearWeather,
count(case when m.extras like '%hat%' then 1 else 0 end) as hatExtras
from VMdata v
inner join files f on v.id = f.id
inner join DMdata d on f.id = d.id
inner join MultiFiledata m on f.id = m.id
where f.filename in (X,Y,Z) group by f.filename;
UNION
SELECT SUM(bikeUser), SUM(pedestrianUser), SUM(clearWeather), SUM(hatExtras)
FROM (
select f.filename,
count(case when v.rUser like '%bike%' then 1 else 0 end) as bikeUser,
count(case when v.rUser like '%Pedestrian%' then 1 else 0 end) as pedestrianUser,
count(case when d.weather like '%clear%' then 1 else 0 end) as clearWeather,
count(case when m.extras like '%hat%' then 1 else 0 end) as hatExtras
from VMdata v
inner join files f on v.id = f.id
inner join DMdata d on f.id = d.id
inner join MultiFiledata m on f.id = m.id
where f.filename in (X,Y,Z) group by f.filename) as summary
hope this helps :)