SQL Vertical grouping - mysql

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;

Related

Sequelize Subtraction of counts

I am trying to get the count by subtracting all records that have false from all the records that have true. I am unable to replicate such a thing in Sequelize but I was able to create it in a raw query:
SELECT (SELECT Count(id)
FROM votes
WHERE up = true
AND id = 1) - (SELECT Count(id)
FROM votes
WHERE up = false
AND id = 1) AS votes
Based off the documentation, I do not see a way to do such a thing. Is there anything that I am missing?
This may be simpler for your js lib to handle
select count(case when up = true then 1 end) - count(case when up = false then 1 end)
from votes
where id = 1
then this previous answer should help I think:
https://stackoverflow.com/a/47397320/2067753
Note that the count() function ignores nulls, but using sum() instead of count can give the same end result:
select sum(case when up = true then 1 else 0 end) - sum(case when up = false then 1 else 0 end)
from votes
where id = 1
using group by:
select id, count(case when up = true then 1 end) - count(case when up = false then 1 end)
from votes
where id = 1
group by id

Appending data from different tables - 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

Is It Possible To Combine These Two Queries

I need the results of Query 2 input in the slot where [Data From Query 2 Should Go Here] is displayed in Query 1. I have currently just been running both queries individually and manually inputting the #'s but is it possible to run all of this as one query?
--Query 1
Select
case
when employee Like 'Ricardo%' Then '38526'
when employee Like 'James%' Then '44187'
else employee
end As [Employee],
COUNT(CASE WHEN d.shipped_status IN ('Hold', 'Waiting Approval') THEN d.ID else null end) As [Processing],
Count(CASE WHEN d.shipped_status = 'Shipped' THEN d.ID ELSE NULL END) As [Shipped],
Count(CASE WHEN d.shipped_status = 'Pending' THEN d.ID ELSE NULL END) As [Pending],
--This is where the Secondary Query Should Go
'' As [Data From Query 2 Should Go Here]
FROM employeeSales
WHERE d.employee IS NOT NULL
GROUP BY
case
when employee Like 'Ricardo%' Then '38526'
when employee Like 'James%' Then '44187'
else employee
end
--Query 2
SELECT
case
when employee Like 'Ricardo%' Then '38526'
when employee Like 'James%' Then '44187'
else employee
end As [Employee],
COUNT(CASE WHEN b.ItemCode = '1400' THEN D.ID ELSE NULL END)
+
COUNT(CASE WHEN a.shipped_status = 'Out Of Stock' THEN a.ID ELSE NULL END)
FROM
inventory a
INNER JOIN itemsordered b
ON a.ID = b.ID
GROUP BY
case
when employee Like 'Ricardo%' Then '38526'
when employee Like 'James%' Then '44187'
else employee
end As [Employee]

How to use user variable as counter with inner join queries that contains GROUP BY statement?

I have 2 tables odds and matches :
matches : has match_id and match_date
odds : has id, timestamp, result, odd_value, user_id, match_id
I had a query that get the following information from those tables for each user:
winnings : the winning bets for each user. (when odds.result = 1)
loses : the lost bets for each user.(when odds.result != 1)
points : the points of each user.(the sum of the odds.odd_value) for each user.
bonus : for each continuous 5 winnings i want to add extra bonus to this variable. (for each user)
How to calculate bonus?
I tried to use this query and I faced a problem : (you can check it here SQL Fiddle)
the calculated bonus are not right for all the users :
first user:(winnings:13, bonus=2).
second user:(winnings:8, bonus=2)bonus here should be 1.
third user:(winnings:14, bonus=3)bonus here should be 2.
why does the query not calculate the bonus correctly?
select d.user_id,
sum(case when d.result = 1 then 1 else 0 end) as winnings,
sum(case when d.result = 2 then 1 else 0 end) as loses,
sum(case when d.result = 1 then d.odd_value else 0 end) as points,
f.bonus
FROM odds d
INNER JOIN
(
SELECT
user_id,SUM(CASE WHEN F1=5 THEN 1 ELSE 0 END) AS bonus
FROM
(
SELECT
user_id,
CASE WHEN result=1 and #counter<5 THEN #counter:=#counter+1 WHEN result=1 and #counter=5 THEN #counter:=1 ELSE #counter:=0 END AS F1
FROM odds o
cross join (SELECT #counter:=0) AS t
INNER JOIN matches mc on mc.match_id = o.match_id
WHERE MONTH(STR_TO_DATE(mc.match_date, '%Y-%m-%d')) = 2 AND
YEAR(STR_TO_DATE(mc.match_date, '%Y-%m-%d')) = 2015 AND
(YEAR(o.timestamp)=2015 AND MONTH(o.timestamp) = 02)
) Temp
group by user_id
)as f on f.user_id = d.user_id
group by d.user_id
I am not sure how your result related to matches table,
you can add back WHERE / INNER JOIN clause if you need.
Here is link to fiddle
and the last iteration according to your comments:
And here is a query:
SET #user:=0;
select d.user_id,
sum(case when d.result = 1 then 1 else 0 end) as winnings,
sum(case when d.result = 2 then 1 else 0 end) as loses,
sum(case when d.result = 1 then d.odd_value else 0 end) as points,
f.bonus
FROM odds d
INNER JOIN
(
SELECT
user_id,SUM(bonus) AS bonus
FROM
(
SELECT
user_id,
CASE WHEN result=1 and #counter<5 AND #user=user_id THEN #counter:=#counter+1
WHEN result=1 and #counter=5 AND #user=user_id THEN #counter:=1
WHEN result=1 and #user<>user_id THEN #counter:=1
ELSE
#counter:=0
END AS F1,
#user:=user_id,
CASE WHEN #counter=5 THEN 1 ELSE 0 END AS bonus
FROM odds o
ORDER BY user_id , match_id
) Temp
group by user_id
)as f on f.user_id = d.user_id
group by d.user_id

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 :)