SQL Server 2014 change multiple string rows to columns - sql

I have below data and it will have multiple titles against multiple genres. Every genre has 7 top titles.
Genre | MarketingTitle
------+----------------
Drama | Drama 1
Drama | Drama 2
...
Drama | Drama 7
I want output to look something like
Genre | Title 1 | Title 2 | Title 3 | ... | Title7
-------+---------+---------+---------+-----+---------
Drama | Drama1 | Drama2 | Drama3 | ... | Drama7
Comedy | Comedy1 | Comedy2 | Comedy3 | ... | Comedy7
I tried pivot table but its just not working
Select
GenreName, [Drama], [Comedy]
from
(select
g.name as GenreName, p.MarketingTitle as MarketingTitle
from
programme p
inner join
Genre g on g.Id = p.GenreId
where
topTitle = 1) c
pivot
(max(MarketingTitle)
for MarketingTitle in ([Drama], [Comedy])
) As pvt
Everything is returned as null and I am pretty sure this query is wrong.
Even below output is desirable but I cant seem to make query work. any help is appreciated.
Drama | Comedy | ... | otherGenres
--------+---------+-----+------------
drama1 | comedy1 | ... |
drama2 | comedy2 | ... |
.. .
drama7 | comedy7 | ... |

Try conditional aggregation instead
select
GenreName, Title1 = max(case when rn = 1 then MarketingTitle end)
, Title2 = max(case when rn = 2 then MarketingTitle end)
, Title3 = max(case when rn = 3 then MarketingTitle end)
, Title4 = max(case when rn = 4 then MarketingTitle end)
, Title5 = max(case when rn = 5 then MarketingTitle end)
, Title6 = max(case when rn = 6 then MarketingTitle end)
, Title7 = max(case when rn = 7 then MarketingTitle end)
from (
select g.name as GenreName, p.MarketingTitle as MarketingTitle
, rn = row_number() over (partition by g.name order by p.MarketingTitle)
from programme p
inner join Genre g on g.Id = p.GenreId
where top = 1
) t
group by GenreName

Related

row counter with condition in two different columns

I have the following tables with sport results (e.g. football):
tblGoals (RowId, GameRowIdm PlayerRowId, TeamRowId, GoalMinute)
RowId | GameRowId | PlayerRowId | TeamRowId | GoalMinute
--------------------------------------------------------
1 | 1 | 1 | 1 | 25
2 | 1 | 2 | 2 | 45
3 | 1 | 3 | 1 | 66
tblPlayers (RowId, PlayerName)
RowId | PlayerName
------------------
1 | John Snow
2 | Frank Underwood
3 | Jack Bauer
tblGames (RowId, TeamHomeRowId, TeamGuestRowId)
RowId | TeamHomeRowId | TeamGuestRowId | GameDate
---------------------------------------------------
1 | 1 | 2 | 2015-01-01
Now I want get a list of all goals. The list should look like this:
GoalMinute | PlayerName | GoalsHome | GoalsGuest
-----------------------------------------------------
25 | John Snow | 1 | 0
45 | Frank Underwood | 1 | 1
66 | Jack Bauer | 2 | 1
GoalsHome and GoalsGuest should be a counter of the shot goals for the team. So e.g. if you check the last row, the result is 2:1 for home team.
To get this list of goals, I used this statement:
SELECT t_gol.GoalMinute,
t_ply.PlayerName,
CASE WHEN
t_gol.TeamRowId = t_gam.TeamHomeRowId
THEN ROW_NUMBER() OVER (PARTITION BY t_gam.TeamHomeRowId ORDER BY t_gam.TeamHomeRowId)
END AS GoalsHome,
CASE WHEN
t_gol.TeamRowId = t_gam.TeamGuestRowId
THEN ROW_NUMBER() OVER (PARTITION BY t_gam.TeamGuestRowId ORDER BY t_gam.TeamGuestRowId)
END AS GoalsGuest
FROM dbo.tblGoalsFussball AS t_gol
LEFT JOIN dbo.tblPlayersFussball AS t_ply ON (t_ply.RowId = t_gol.PlayerRowId)
LEFT JOIN dbo.tblGames AS t_gam ON (t_gam.RowId = t_gol.GameRowId)
WHERE t_gol.GameRowId = #match_row
But what I get is this here:
GoalMinute | PlayerName | GoalsHome | GoalsGuest
-----------------------------------------------------
25 | John Snow | 1 | NULL
45 | Frank Underwood | NULL | 2
66 | Jack Bauer | 3 | NULL
Maybe ROW_NUMBER() is the wrong approach?
I would do the running total using sum() as a windowed aggregate function with the over ... clause, which works in SQL Server 2012+.
select
g.RowId, g.GameDate, t.GoalMinute, p.PlayerName,
GoalsHome = COALESCE(SUM(case when TeamRowId = g.TeamHomeRowId then 1 end) OVER (PARTITION BY gamerowid ORDER BY goalminute),0),
GoalsGuest = COALESCE(SUM(case when TeamRowId = g.TeamGuestRowId then 1 end) OVER (PARTITION BY gamerowid ORDER BY goalminute),0)
from tblGoals t
join tblPlayers p on t.PlayerRowId = p.RowId
join tblGames g on t.GameRowId = g.RowId
order by t.GameRowId, t.GoalMinute
Another approach (that also works in older versions) is to use a self-join and sum up the rows with lower goalminutes. For ease of reading I've used a common table expression to split the goals into two columns for home and guest team:
;with t as (
select
g.GoalMinute, g.PlayerRowId, g.GameRowId,
case when TeamRowId = ga.TeamHomeRowId then 1 end HomeGoals,
case when TeamRowId = ga.TeamGuestRowId then 1 end GuestGoals
from tblGoals g
join tblGames ga on g.GameRowId = ga.RowId
)
select
g.RowId, g.GameDate, t.GoalMinute, p.PlayerName,
GoalsHome = (select sum(coalesce(HomeGoals,0)) from t t2 where t2.GoalMinute <= t.GoalMinute and t2.GameRowId = t.GameRowId),
GoalsGuest = (select sum(coalesce(GuestGoals,0)) from t t2 where t2.GoalMinute <= t.GoalMinute and t2.GameRowId = t.GameRowId)
from t
join tblPlayers p on t.PlayerRowId = p.RowId
join tblGames g on t.GameRowId = g.RowId
order by t.GameRowId, t.GoalMinute
The CTE isn't necessary though, you could just as well use a derived table
Sample SQL Fiddle
I think the easiest way is with subqueries..
SELECT
tgs.GoalMinute,
tpl.PlayerName,
( SELECT
COUNT(t.RowId)
FROM
tblgoals AS t
WHERE t.GoalMinute <= tgs.GoalMinute
AND t.GameRowId = tgm.RowId
AND t.TeamRowId = tgm.TeamHomeRowId
) AS HomeGoals,
( SELECT
COUNT(t.RowId)
FROM
tblgoals AS t
WHERE t.GoalMinute <= tgs.GoalMinute
AND t.GameRowId = tgm.RowId
AND t.TeamRowId = tgm.TeamGuestRowId
) AS GuestGoals
FROM
tblgoals AS tgs
JOIN tblplayers AS tpl ON tgs.RowId = tpl.RowId
JOIN tblGames AS tgm ON tgm.RowId = tgs.GameRowId
ORDER BY tgs.GoalMinute

SQL Finding multiple values difficulty

I'm trying to generate the correct SQL for a project.
Here is a sample dataset:
DateTime | EmpID | Function | Location
--------------------------------------------------
1/23/2015 2:00PM | 123 | 1 | 1
1/23/2015 2:10PM | 123 | 2 | 1
1/23/2015 2:20PM | 123 | 1 | 2
1/23/2015 2:40PM | 123 | 2 | 2
1/24/2015 2:00PM | 321 | 1 | 2
1/24/2015 2:15PM | 321 | 2 | 2
1/24/2015 2:30PM | 321 | 1 | 3
I need to pull a count of all records where functionid = 1 and location MUST EQUAL both 1 and 2. So the first row and the third row would be returned and considered a count of 1.
Hopefully I'm making sense with this. Basically I need to know how many times an employee was at two locations. Any help would be appreciated.
Group by EmpId and count locations.
SELECT *
FROM MyTable T1
WHERE Function = 1 AND
NOT EXISTS (SELECT 1
FROM MyTable T2
WHERE T1.EmpId = T2.EmpId AND
T1.Function = T2.Function AND
T2.Location NOT IN (1, 2))
GROUP BY EmpId
HAVING Count(DISTINCT Location) > 1
Have not tested it but think this will work
SELECT EmpID, COUNT(EmpID) AS NumOfTimes
From [Table Name]
WHERE FunctionID = 1 AND (Location = 1 OR Location = 2)
GROUP BY EmpID
HAVING NumOfTimes = 2
SELECT EmpID , COUNT(*) total, COUNT (CASE WHEN Location = 1 THEN 1 END) was_in_1,COUNT (CASE WHEN Location = 2 THEN 1 END) was_in_2
FROM table
WHERE Function = 1
GROUP BY EmpID
HAVING MAX(CASE WHEN Location = 1 THEN 1 ELSE 0 END) = MAX(CASE WHEN Location = 2 THEN 1 ELSE 0 END)
but if you would like to know if the employee was in any 2 locations then jarlh gave the right comment
group by EmpID
having count(distinct location) >= 2
SELECT A.EmpID, COUNT(*)
FROM YOURTABLENAME A
INNER JOIN YOURTABLENAME B
ON A.EmpID= B.EmpID
WHERE A.Location = 1 and B.Location = 2
and A.Function = B.Function and A.Function = 1
GROUP BY A.EmpID

SQL database tag request

I have a photo table which looks like this:
id title rating photopath
1 myself 7.0 /photopath1.jpg
2 cat 8.0 /photopath2.jpg
3 dog 6.0 /photopath3.jpg
4 girlfriend 5.0 /photopath4.jpg
and a tag table:
id tag_name photo_id
1 selfie 1
2 sun 1
3 nature 2
4 relax 2
5 loyal 3
6 journal 3
7 selfie 4
8 sun 4
9 problems 4
And I want to call all photos that have the tags "selfie" and "sun". How do I do that?
This is a "set-within-sets" query. I like to solve these using aggregation and having:
select p.id
from photos p join
tags t
on p.id = t.photo_id
group by p.id
having sum(case when tag_name = 'selfie' then 1 else 0 end) > 0 and
sum(case when tag_name = 'sun' then 1 else 0 end) > 0;
The convenience of this method is that it is easy to add more conditions (such as another tag) or to invert a condition (such as "selfie"s without "sun").
select p.*
from photo p
join (select p.id
from photo p
join tag t
on p.id = t.photo_id
where t.tag_name in ('selfie', 'sun')
group by p.id
having count(*) = 2) x
on p.id = x.id
Fiddle: http://sqlfiddle.com/#!2/5c95c2/2/0
Output:
| ID | TITLE | RATING | PHOTOGRAPH |
|----|------------|--------|------------------|
| 1 | myself | 7 | /photograph1.jpg |
| 4 | girlfriend | 5 | /photograph4.jpg |
SELECT *
FROM Photos
WHERE EXISTS (SELECT 1
FROM Tag_Table
WHERE photo_id = Photos.id
AND tag_name = 'selfie'
)
AND EXISTS (SELECT 1
FROM Tag_Table
WHERE photo_id = Photos.id
AND tag_name = 'Sun'
)

sql how to transform data vertically

I have a 3 datbles Dealer, payment_type and dealer_payment_type
Dealer : dealer_id , dealer_name, dealer_address
1 | test | 123 test lane
2 | abc | abc lane
3 | def | def lane
Payment_type : paymenttype_id , paytype
1 | CHECK
2 | WIRE
3 | CREDIT
Dealer_Payment_type : DPT_id , dealer_id , payment_type_id
1 | 1 | 1
2 | 1 | 2
3 | 1 | 3
4 | 2 | 2
5 | 2 | 3
6 | 3 | 1
7 | 3 | 2
I have to write a query to get payment type info for each dealer , query needs to return data like this:
dealer_id , dealer_name , paytype
1 | test | check,wire,credit
2 | abc | wire,credit
3 | def | check,wire
OR
dealer_id , dealer_name , check , wire , credit
1 | test | true | true | true
2 | abc | false | true | true
3 | def | true | false | true
You did not specify what version of Oracle you are using.
If you are using Oracle 11g, then you can use the following.
To get the values into a single column, then you can use LISTAGG:
select d.dealer_id,
d.dealer_name,
listagg(p.paytype, ',') within group (order by d.dealer_id) as paytype
from dealer d
left join Dealer_Payment_type dp
on d.dealer_id = dp.dealer_id
left join payment_type p
on dp.payment_type_id = p.paymenttype_id
group by d.dealer_id, d.dealer_name;
See SQL Fiddle with demo
To get the values in separate columns, then you can use PIVOT:
select dealer_id, dealer_name,
coalesce("Check", 'false') "Check",
coalesce("Wire", 'false') "Wire",
coalesce("Credit", 'false') "Credit"
from
(
select d.dealer_id,
d.dealer_name,
p.paytype,
'true' flag
from dealer d
left join Dealer_Payment_type dp
on d.dealer_id = dp.dealer_id
left join payment_type p
on dp.payment_type_id = p.paymenttype_id
)
pivot
(
max(flag)
for paytype in ('CHECK' as "Check", 'WIRE' as "Wire", 'CREDIT' as "Credit")
)
See SQL Fiddle with Demo.
If you are not using Oracle 11g, then you can use wm_concat() to concatenate the values into a single row:
select d.dealer_id,
d.dealer_name,
wm_concat(p.paytype) as paytype
from dealer d
left join Dealer_Payment_type dp
on d.dealer_id = dp.dealer_id
left join payment_type p
on dp.payment_type_id = p.paymenttype_id
group by d.dealer_id, d.dealer_name;
To create the separate columns, then you can use an aggregate function with a CASE:
select dealer_id, dealer_name,
max(case when paytype = 'CHECK' then flag else 'false' end) "Check",
max(case when paytype = 'WIRE' then flag else 'false' end) "Wire",
max(case when paytype = 'CREDIT' then flag else 'false' end) "Credit"
from
(
select d.dealer_id,
d.dealer_name,
p.paytype,
'true' flag
from dealer d
left join Dealer_Payment_type dp
on d.dealer_id = dp.dealer_id
left join payment_type p
on dp.payment_type_id = p.paymenttype_id
)
group by dealer_id, dealer_name;
See SQL Fiddle with Demo

Help with a SQL query joining multiple tables

First I will explain the case, I have a table tbl_game with a structure as such. This table contains, the time where the game was started and pair playing the game
| id | time | pair_id |
-----------+--------------+ ---------------
1 | 123123123 | 1 |
2 | 123168877 | 1 |
and I have another table tbl_throws which holds the score for each player. In case you are wondering, this a basic dice rolling game
| id | game_id | player_id | score |
-----------+--------------+---------------+---------|
| 1 | 1 | 1 | 2 |
| 2 | 1 | 2 | 5 |
| 3 | 1 | 1 | 9 |
| 4 | 1 | 2 | 11 |
| 5 | 2 | 1 | 7 |
| 6 | 2 | 2 | 6 |
Now, id here is the throw id, not the game id. Here each player with player_id 1 and 2 has throws the dice twice and got the respective score as presented all in same game and just one time in another
Now, using these two table, I need to create a record set, that the total score of each player in one game
| game_id | game_time | player1_total | player2_total|
|------------+-----------+---------------+--------------|
| 1 | 123123123 | 11 | 16 |
| 2 | 123168877 | 7 | 6 |
I tried lots of mumbo jumbo queries, but nothing is giving corrent result?
What is the correct query for this?
Update
Since, most of the answers were bounded by a fact that, player1id and player2id had to be known or fixed.
So may be the information I am about to provide will help to clear the confusion.
there is another table, which holds the information of the player. tbl_pupil
Structure is like the following
| id | unique_id | name |
|---------+---------------+----------|
| 1 | 001 | some |
| 2 | 002 | another |
and these player are collectively called, a pair in another table tbl_pair
| id | player1 | player2 |
|---------+---------------+----------|
| 1 | 1 | 2 |
So, now
select
g.id
g.time
p1.id as player1id
p1.name as player1name
t.score as player1score
p2.id as player2id
p2.name as player2name
t.score as player2score
FROM
tbl_game g,
inner join tbl_pair as pair on g.pair_id = pair.id
inner join tbl_pupil as p1 on p1.id = pair.player1
inner join tbl_pupil as p2 on p2.id = pair.player2
inner join tbl_throw as t on g.id = t.game_id
This is my preliminary query, which brings the record set, on a way as such
| id | time | player1id | player1name | player1score | player2id | player2name | player2score |
----------------------------------------------------------------------------------------------------------------------------
| 1 | 12 | 1 | some | 5 | 2 | another | 2 |
| 1 | 12 | 1 | some | 5 | 2 | another | 5 |
| 1 | 12 | 1 | some | 9 | 2 | another | 9 |
| 1 | 12 | 1 | some | 11 | 2 | another | 11 |
Now I am just showing the results of one game id by the way. I don't save sufficient knowledge, to group the above record into one, with player1 separate sum score in one column and playe2's separate sum of score in another column.
Try this:
SELECT
tbl_game.id AS game_id,
tbl_game.time AS game_time,
SUM(CASE WHEN player_id = 1 THEN score ELSE 0 END) AS player1_total,
SUM(CASE WHEN player_id = 2 THEN score ELSE 0 END) AS player2_total
FROM tbl_game JOIN tbl_thorws ON tbl_game.id = tbl_throws.game_id
GROUP BY tbl_game.id
This is similar to some of the other answers, but crucially doesn't depend on the player IDs being 1 and 2.
select
game_id = g.id,
game_time = g.time,
player1_total = SUM(case t.player_id when p.player1_id then t.score else 0 end),
player2_total = SUM(case t.player_id when p.player2_id then t.score else 0 end)
from
tbl_game g
join tbl_throws t on g.id = t.game_id
join ( --Get the player IDs for this game
select
game_id,
player1_id = MIN(player_id),
player2_id = MAX(player_id)
from
tbl_throws
group by game_id
) p
on p.game_id = t.game_id
group by
g.id, g.time
Just for fun I've generalized the above out to allow more > 2 players:
The 2 CTE tables just show the test data I'm using
;WITH tbl_game as (
select ID = 1, time = 123123123, pair_id = 1
union select ID = 2, time = 123168877, pair_id = 1
),
tbl_throws as (
select id = 1, game_id = 1, player_id = 1, score = 2
union select id = 2, game_id = 1, player_id = 2, score = 5
union select id = 2, game_id = 1, player_id = 3, score = 5
union select id = 3, game_id = 1, player_id = 1, score = 9
union select id = 4, game_id = 1, player_id = 2, score = 11
union select id = 5, game_id = 2, player_id = 1, score = 7
union select id = 6, game_id = 2, player_id = 2, score = 6
)
select
game_id = g.id,
game_time = g.time,
player1_id = MAX(case x.player_no when 1 then t.player_id else 0 end),
player1_total = SUM(case x.player_no when 1 then t.score else 0 end),
player1_id = MAX(case x.player_no when 2 then t.player_id else 0 end),
player2_total = SUM(case x.player_no when 2 then t.score else 0 end),
player3_id = MAX(case x.player_no when 3 then t.player_id else 0 end),
player3_total = SUM(case x.player_no when 3 then t.score else 0 end),
player4_id = MAX(case x.player_no when 4 then t.player_id else 0 end),
player4_total = SUM(case x.player_no when 4 then t.score else 0 end)
/* Add more rows for the number of players permitted in a single game */
from
tbl_game g
join tbl_throws t on g.id = t.game_id
cross apply (
select player_no = COUNT(distinct player_id)
from tbl_throws sub
where sub.player_id <= t.player_id
and Sub.game_id = t.game_id
) x
group by
g.id, g.time
You need to inner join the two tables, and aggregate your scores. To do the basic pivot you are after I used a CASE statement to aggregate by player.
SELECT G.Id as Game_Id,
G.time as Game_Time,
SUM(CASE WHEN t.Player_id = 1 THEN t.score ELSE 0 END) as Player1_total,
SUM(CASE WHEN t.Player_id = 2 THEN t.score ELSE 0 END) as Player2_total
FROM tbl_game G
INNER JOIN tbl_throws T
ON g.id = t.game_id
GROUP BY g.ID, g.time
I think this should do pretty much what you want to do.
SELECT
tbl_game.id as game_id,
tbl_game.time as game_time,
SUM(player1.score) as player1_total,
SUM(player2.score) as player2_total
FROM tbl_game
INNER JOIN tbl_throws player1 ON player1.game_id = tbl_game.id AND player1.player_id = 1
INNER JOIN tbl_throws player2 ON player2.game_id = tbl_game.id AND player2.player_id = 2
GROUP BY tbl_game.id, tbl_game.time
SELECT t.game_id
, t.game_time
, ( SELECT SUM(t.score)
FROM tbl_throws AS t
WHERE t.game_id = g.id
AND player_id = 1
) AS player1_total
, ( SELECT SUM(t.score)
FROM tbl_throws AS t
WHERE t.game_id = g.id
AND player_id = 2
) AS player2_total
FROM tbl_game AS g