SQL database tag request - sql

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

Related

Need help for MS Access Select Request using 2 tables

For a "products reservation system", I have 2 tables :
"RD", for global reservations data (fieds: ID, CustomerID, Date, ...)
"RP", for reserved products data per reservation (fields: ID, RD_ID, ProductID, Status, ...). RD_ID fits with the ID in RD table (field for joining). Status field can have these values: O, C, S.
I need to extract (with 2 Select instructions) the list of reservations and the number of reservations for which all products have status 'O' .
Data example for RP:
ID | RD_ID | ProdID | Status
----------------------------
1 | 1 | 100 | O
2 | 1 | 101 | O
3 | 1 | 102 | O
4 | 2 | 105 | O
5 | 2 | 100 | S
6 | 3 | 101 | C
7 | 3 | 102 | O
In this example, Select statement should return only RD_ID 1
For the number of ID, the following request does not work because it also includes reservations with products having different status:
SELECT COUNT(rd.ID) FROM rd INNER JOIN rp ON rp.RD_ID = rd.ID WHERE rp.Status = 'O';
Could you help me for the right Select statement?
Thank you.
SELECT rd.ID, COUNT(rd.ID) CountOfRD, status
FROM rd INNER JOIN rp ON rp.RD_ID
GROUP BY rd.ID, status
Use not exists as follows:
Select t.* from your_table t
Where t.status = 'O'
And not exists (select 1 from your_table tt
Where t.rd_id = tt.rd_id
And t.status != tt.status)
You can also use group by and having as follows:
Select rd_id
From your_table t
Group by rd_id
Having sum(case when status <> 'O' then 1 end) > 0

SQL Server 2014 change multiple string rows to columns

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

Count / sum values in subquery and order by it

I have tables like below:
user
id | status
1 | 0
gallery
id | status | create_by_user_id
1 | 0 | 1
2 | 0 | 1
3 | 0 | 1
media
id | status
1 | 0
2 | 0
3 | 0
gallery_media
fk gallery.id fk media.id
id | gallery_id | media_id | sequence
1 | 1 | 1 | 1
2 | 2 | 2 | 1
3 | 2 | 3 | 2
monitor_traffic
1:gallery 2:media
id | anonymous_id | user_id | endpoint_code | endpoint_id
1 | 1 | | 1 | 2 gallery.id 2
2 | 2 | | 1 | 2 gallery.id 2
3 | | 1 | 2 | 3 media.id 3 include in gallery.id 2
these means gallery.id 2 contain 3 rows
gallery_information
fk gallery.id
id | gallery_id
gallery includes media.
monitor_traffic.endpoint_code: 1 .. gallery; 2 .. media
If 1 then monitor_traffic.endpoint_id references gallery.id
monitor_traffic.user_id, monitor_traffic.anonymous_id integer or null
Objective
I want to output gallery rows sort by count each gallery rows in monitor_traffic, then count the gallery related media rows in monitor_traffic. Finally sum them.
The query I provide only counts media in monitor_traffic without summing them and also does not count gallery in monitor_traffic.
How to do this?
This is part of a function, input option then output build query, something like this. I hope to find a solution (maybe with a subquery) that does not require to change other parts of the query.
Query:
SELECT
g.*,
row_to_json(gi.*) as gallery_information
FROM gallery g
LEFT JOIN gallery_information gi ON gi.gallery_id = g.id
LEFT JOIN "user" u ON u.id = g.create_by_user_id
-- start
LEFT JOIN gallery_media gm ON gm.gallery_id = g.id
LEFT JOIN (
SELECT
endpoint_id,
COUNT(*) as mt_count
FROM monitor_traffic
WHERE endpoint_code = 2
GROUP BY endpoint_id
) mt ON mt.endpoint_id = m.id
-- end
ORDER BY mt.mt_count desc NULLS LAST;
sql fiddle
I suggest a CTE to count both types in one aggregation and join to it two times in the FROM clause:
WITH mt AS ( -- count once for both media and gallery
SELECT endpoint_code, endpoint_id, count(*) AS ct
FROM monitor_traffic
GROUP BY 1, 2
)
SELECT g.*, row_to_json(gi.*) AS gallery_information
FROM gallery g
LEFT JOIN mt ON mt.endpoint_id = g.id -- 1st join to mt
AND mt.endpoint_code = 1 -- gallery
LEFT JOIN (
SELECT gm.gallery_id, sum(ct) AS ct
FROM gallery_media gm
JOIN mt ON mt.endpoint_id = gm.media_id -- 2nd join to mt
AND mt.endpoint_code = 2 -- media
GROUP BY 1
) mmt ON mmt.gallery_id = g.id
LEFT JOIN gallery_information gi ON gi.gallery_id = g.id
ORDER BY mt.ct DESC NULLS LAST -- count of galleries
, mmt.ct DESC NULLS LAST; -- count of "gallery related media"
Or, to order by the sum of both counts:
...
ORDER BY COALESCE(mt.ct, 0) + COALESCE(mmt.ct, 0) DESC;
Aggregate first, then join. That prevents complications with "proxy-cross joins" that multiply rows:
Two SQL LEFT JOINS produce incorrect result
The LEFT JOIN to "user" seems to be dead freight. Remove it:
LEFT JOIN "user" u ON u.id = g.create_by_user_id
Don't use reserved words like "user" as identifier, even if that's allowed as long as you double-quote. Very error-prone.

SQL: Querying 2 tables and sort results by values in another table

Is it possible to select all the comments for a single record and order them by the overall ratio of positive:negative votes from another table?
I think I may need to use a subselect but I really am not sure how or where.
2 tables:
table 1 [Comments]
ID | RecordID | Comment
------------------------------
1 | 100001 | blah blah
2 | 100202 | another co
3 | 100054 | lorem ips
table 2 [Ratings]
ID | CommentID | Vote
-------------------------
1 | 1 | 1
2 | 1 | 0
3 | 1 | 1
4 | 3 | 0
5 | 3 | 0
6 | 3 | 0
Please note: 'Vote' : 0 = negative vote; 1 = positive vote
Also, I am using Microsoft SQL Server
Thanks for any help :)
To sort them from positive to negative rating you can do:
SELECT c.id,
c.recordID,
c.comment
FROM comments c
INNER JOIN (
SELECT CommentID,
SUM(CASE WHEN Vote = 0 THEN -1 ELSE 1 END) AS RATING FROM ratings
GROUP BY commentID
) r ON r.commentID = c.id
ORDER BY r.RATING DESC;
sqlfiddle demo
Note that i am using the CASE to give votes with 0 a value of -1, otherwise, 10 negatives and one positive would give a rating of 1.
This query is not taking into account the comments without scores. If you want to have the comments without scores to have 0 rating, you could do:
SELECT c.id,
c.recordID,
c.comment
FROM comments c
LEFT JOIN (
SELECT CommentID,
SUM(CASE WHEN Vote = 0 THEN -1 ELSE 1 END) AS RATING FROM ratings
GROUP BY commentID
) r ON r.commentID = c.id
ORDER BY COALESCE(r.RATING,0) DESC;
EDIT:
To get the ratio try this:
SELECT c.id,
c.recordID,
c.comment,
r.rating
FROM comments c
LEFT JOIN (
SELECT CommentID,
SUM(CASE
WHEN Vote = 1
THEN 1
ELSE 0
END) / (nullif(COUNT(*) * 1.0, 0)) AS RATING
FROM ratings
GROUP BY commentID
) r ON r.commentID = c.id
ORDER BY COALESCE(r.RATING, 0) DESC;
sqlfiddle 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