Denormalization table - SQL Select (Pivot Table?) - sql

CAMPAIGN table
ID
campaign_name
1
Campaign A
2
Campaign B
PARTICIPANT table
ID
campaign_id
participant_name
1
1
Alice
2
1
Ben
CUSTOM_FIELD table
ID
campaign_id
field_name
1
1
Gender
2
1
Age
FIELD_ANSWER table
ID
participant_id
field_id
answer
1
1
1
Female
2
1
2
24
3
2
1
Male
4
2
2
28
With these tables in above, can we query a result as shown below?
Campaign Name
Participant Name
Gender
Age
Campaign A
Alice
Female
24
Campaign A
Ben
Male
28

Using pivoting logic we can try:
SELECT
c.campaign_name,
p.participant_name,
MAX(CASE WHEN cf.field_name = 'Gender' THEN fa.answer END) AS Gender,
MAX(CASE WHEN cf.field_name = 'Age' THEN fa.answer END) AS Age
FROM CAMPAIGN c
INNER JOIN PARTICIPANT p
ON p.campaign_id = c.ID
INNER JOIN FIELD_ANSWER fa
ON fa.participant_id = p.ID
INNER JOIN CUSTOM_FIELD cf
ON cf.ID = fa.field_id AND cf.campaign_id = c.ID
GROUP BY
c.campaign_name,
p.participant_name;
Here is a demo in SQL Server, though the above query should run on most other database as well.

Related

How to make a view with 2 tables linked by another table?

I have 3 table
driver
id
first name
last_name
age
1
Joe
doe
26
2
John
smith
31
...
...
...
...
Location
id
name
zipcode
country
1
London
250 329
UK
2
NY
00501
USA
...
...
...
...
Travel
id
Person_id
location_id
nb_passenger
1
1
1
2
2
1
1
4
3
2
2
3
4
1
2
2
5
2
1
3
...
...
...
...
I'm looking to create a view to get the total number of passengers by destination and by driver, but it's taking the same row multiple times
I tried something like that but it didn't work
SELECT location.id AS location_id,
location.name AS location_name,
d.id AS driver_id,
d.name AS driver_name,
sum(tr.passenger) as passenger_count
FROM location
LEFT JOIN travel tr_0 ON location.id = tr_0.location_id
LEFT JOIN driver d ON tr_0.driver_id = d.id
LEFT JOIN travel tr ON location.id = tr.location_id AND tr.driver_id = d.id
GROUP BY location.id, location.name,...;
I'm sure it's simple but I'm not on the right way

PostreSQL filter results by subset of a joined table and group them

I have table of people, another table of cars and a third table to join them since they have a many to many relationship. I want to select people who own a certain set of cars and group them by a region property on the person. So for example I would want to find all American's who own a Honda and a Nissan.
Example:
people table
id name region
1 Jon America
2 Jane Europe
3 Mike America
cars table
id make
1 Honda
2 Toyota
3 Nissan
people_cars table
person_id car_id
1 1
1 3
2 2
3 1
Desired result:
region own_honda_and_nissan
America 1
Europe 0
An idea for a SQL expression I have is:
SELECT
people.region,
CASE WHEN SUM(CASE WHEN cars.name IN ('Honda', 'Nissan') THEN 1 ELSE 0 END) = 2 THEN 1 ELSE 0 AS own_honda_and_nissan
FROM people
JOIN people_cars ON people_cars.person_id = people.id
JOIN cars ON people_cars.car_id = cars.id
GROUP BY people.region
HAVING
SUM(CASE WHEN cars.name IN ('Honda', 'Nissan') THEN 1 ELSE 0 END) = 2
ORDER BY own_honda_and_nissan DESC
This works if you group by people.id but once they get grouped by region it no longer works.
Use two levels of aggregation:
SELECT p.pregion, COUNT(*) as own_honda_and_nissan
FROM (SELECT pid, p.region,
FROM people p JOIN
people_cars pc
ON pc.person_id = p.id JOIN
cars c
ON pc.car_id = c.id
WHERE c.name IN ('Honda', 'Nissan')
GROUP BY p.id, p.region
HAVING COUNT(DISTINCT c.name) = 2
) p
GROUP BY p.region
ORDER BY own_honda_and_nissan DESC

Query to show one column multiplied

I have the following 3 tables;
Table_Names:
user_id Name
------------------
1 Mark
2 Tom
3 Ana
Table_Language:
language_id Language
-----------------------
1 English
2 German
Table_Name_Lang
id user_id language_id
---------------------------
1 1 1
2 1 2
3 2 1
4 3 2
How can I create a query to show the expected results like those below?
Name Expr_1_Eng Expr_1_Ger
---------------------------------
Mark English German
Tom English
Ana German
Thanks Tok
I would do:
select n.name,
max(case when l.language = 'English' then l.language end) as has_English,
max(case when l.language = 'German' then l.language end) as has_German
from names n join
name_lang nl
on nl.user_id = n.user_id join
lang l
on nl.language_id = l.language_id
group by n.name
You can try to use JOIN with condition aggregate function
SELECT Name,
MAX(CASE WHEN tnl.language_id = 1 then tl.Language end),
MAX(CASE WHEN tnl.language_id = 2 then tl.Language end)
FROM
Table_Name_Lang tnl
JOIN Table_Names tn on tnl.language_id = tn.language_id
JOIN Table_Language tl on tl.user_id = tnl.user_id
GROUP BY Name
I think you should use pivot:
select n.name,[0] as language_one,[1] as language_two,[2],[3] from
(select n.name,tl.language
tablename n
left join Table_Name_Lang tnl on n.userid=tnl.userid
left join table_language tl on tl.id=tnl.id
group by n.name,tl.language
)T
pivot
(
language
for name in [0],[1],[2],[3]
)AS PivotTable;

SQL Query For These Two Tables

I have these two tables:
Person
Name Gender RegionID
Bob Male 1
Mary Female 2
Jane Female 3
Chris Male 1
Paul Male 2
Matt Male 2
Jenny Female 1
Region
ID Region
1 North
2 South
3 Central
I would like to end up with a table like this:
Region Male Female
North 2 1
South 2 1
Central 0 1
I first tried to get the resulting table with the region's ID in there so no join required:
select RegionID, Gender, count(Name) from Person group by RegionID
But the Gender field is coming back undefined. And didn't really know where to go from there.
As tagged, I'm using the alasql JavaScript library.
Try to look at this:
SELECT A.Region
, SUM(CASE WHEN Gender = 'Male' Then 1 else 0 end) Male
, SUM(CASE WHEN Gender = 'Female' Then 1 else 0 end) Female
FROM Region A
Inner Join Person B
On A.ID = B.RegionID
Group By A.Region
You can use this
select region
,sum(case when b.Gender='Male' then 1 else 0 end) Male
,sum(case when b.Gender='Female' then 1 else 0 end) Female
from Region a
inner join Person b on a.[Id]=b.RegionID
group by a.[Id],a.Region
order by [id]
In tsql you can pivot, which might be easier if you have more than a couple of values to pivot on:
Select * from
( select Region ,Gender
from Region a
inner join Person b on a.[Id]=b.RegionID ) p
pivot
( count(Gender)
for Gender
IN ([Male],[Female])
) pivottable
If you add a third gender, nonbinary like this
insert into Person values ('Kim','Nonbinary',3)
You can easily extend you query to account for that:
Select * from
( select Region ,Gender
from Region a
inner join Person b on a.[Id]=b.RegionID ) p
pivot
( count(Gender)
for Gender
IN ([Male],[Female],[Nonbinary])
) pivottable
order by a.[id]
For anyone looking for the final solution in alasql, this is what I came up with thanks to the help from this page:
var res = alasql("SELECT regionData.Title \
, SUM(CASE WHEN peopleData.Gender = 'Male' THEN 1 ELSE 0 END) AS Male \
, SUM(CASE WHEN peopleData.Gender = 'Female' THEN 1 ELSE 0 END) AS Female \
FROM ? regionData LEFT JOIN ? peopleData \
ON regionData.ID = peopleData.RegionID\
GROUP BY regionData.Title", [regionData, bensData]);

SQL: Select from many-many through, joined on conditions in through table

Can't get my head around this...
I have 3 tables like this:
Computers
---------
Id
Name
ComputerLogins
--------------
Computer_Id
User_Id
NumberOfLogins
Users
-----
Id
Name
Computers "have and belong to many" Users "through" ComputerLogins.
Sample data:
Computers: Id Name
1 "Alpha"
2 "Beta"
3 "Gamma"
Users: Id Name
1 "Joe"
2 "Fred"
ComputerLogins: Computer_Id User_Id NumberOfLogins
1 1 5
1 2 12
2 1 10
2 2 6
3 1 2
3 2 4
I'm trying to construct a view that will output one row for each record in Computers, and join a Users row through MAX(NumberOfLogins) in ComputerLogins.
Desired output:
Computer_Id User_Id NumberOfLogins
1 2 12
2 1 10
3 2 4
Can you suggest a view query that will produce the desired output?
Thanks!
SELECT
CL.*, U.* --change this as needed
FROM
(
SELECT
Computer_ID, MAX(NumberOfLogins) AS NumberOfLogins
FROM
ComputerLogins
GROUP BY
Computer_ID
) maxC
JOIN
ComputerLogins CL On maxC.Computer_ID = CL.Computer_ID AND maxC.NumberOfLogins = CL.NumberOfLogins
JOIN
Users U On CL.User_ID = U.ID
Wrap in a view etc
Use:
CREATE VIEW your_view AS
SELECT c.id AS computer_id,
u.id AS user_id,
COUNT(*) AS NumberOfLogins
FROM COMPUTERS c
JOIN COMPUTERLOGINS cl ON cl.computer_id = c.id
JOIN USERS u ON u.id = cl.user_id
GROUP BY c.id, u.id