问题
I am needing some help with SQL syntax.
Say I have a members table, a questions table and an answers table, basics of the tables are as follows:
Members table:
memberId: primary key
Questions table:
questionId: primary key,
questionText:varchar
Answers table:
answerId: primary key,
questionId: int (relating to the row of the question in the questions table)
memberId: int (relating to the row of the member in the members table)
answerValue:varchar
The tables will have more columns but for the purpose of this problem these should suffice.
Now In some instances I will want to run a query that will return a distinct list of member ids where the members answers to a list of questions match a certain value.
For instance:
Say there is a question in the questions table (row 1) with the question text: "Do you like cats or dogs?":
questionId questionText
----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
1 Do you like cats or dogs?
Then there are 10 members in the members table from id 1 to 10, and corresponding answers in the answers table:
answerid questionId memberId answerId
---------------------------------------------------------------------------------------
1 1 1 cats
2 1 2 both
3 1 3 cats
4 1 4 cats
5 1 5 cats
6 1 6 dogs
7 1 7 dogs
8 1 8 dogs
9 1 9 dogs
10 1 10 both
It would be simple enough to query with this for example, those who answered dogs:
SELECT DISTINCT memberId FROM members INNER JOIN answers ON members.memberId = answers.answerId WHERE answers.questionId = 1 AND answers.answerValue = 'dogs'
This would return:
memberId
---------------
6
7
8
9
But what if I were to add another question to the questions table:
questionId questionText
----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
1 Do you like cats or dogs?
2 What is your favourite color?
And the answers table was updated as follows:
answerid questionId memberId answerId
---------------------------------------------------------------------------------------
1 1 1 cats
2 1 2 both
3 1 3 cats
4 1 4 cats
5 1 5 cats
6 1 6 dogs
7 1 7 dogs
8 1 8 dogs
9 1 9 dogs
10 1 10 both
11 2 1 blue
12 2 2 red
13 2 3 green
14 2 5 green
15 2 4 black
16 2 6 violet
17 2 7 pink
18 2 8 green
19 2 9 red
20 2 10 yellow
How would I query for multiple questions?
What I am looking for is a set syntax that would work for querying a list of distinct members who have answered specific answers to any number of questions, for instance:
The query for members who like ((dogs AND red) OR (cats AND green)) should return:
memberId
-----------------
9
3
5
There could be any combination of answers. The essence of it is how to I query against multiple values for an inner joined table?
Thanks for any help anyone can give, sorry if its a little confusing.
回答1:
You can select the individual Ids who answered one of the questions, like this:
select distinct memberId where answerId = 'dogs'
select distinct memberId where answerId = 'red'
Then replace the ands with inner joins, like this:
select memberId from
(select distinct memberId where answerId = 'dogs') D
inner join
(select distinct memberId where answerId = 'red') R
on D.MemberId = R.memberId
And replace the OR with UNION, like this:
select memberId from
(select distinct memberId where answerId = 'dogs') D
inner join
(select distinct memberId where answerId = 'red') R
on D.MemberId = R.memberId
union
select memberId from
(select distinct memberId where answerId = 'cats') D
inner join
(select distinct memberId where answerId = 'green') R
on D.MemberId = R.memberId
This is exactly, as you say, a "set sintax":
Set: each indivdual select
Intersection of sets : inner join
Union of sets: union
If you need extra conditions, for example checking only answers 1 and 2, you can use a CTE with the common conditions like "answerId in (1,2)" to avoid writing this same condition in every select, making the sintax more clear.
You can also use this sintax, which is more similar to your original writing:
select memberId from members M
where
( exists (select 1 from answers where answerId='dogs' and memberId=M.member Id)
and exists (select 1 from answers where answerId='red' and memberId=M.member Id) )
or
( exists (select 1 from answers where answerId='cats' and memberId=M.member Id)
and exists (select 1 from answers where answerId='green' and memberId=M.member Id) )
-- I select 1 instead of * or any column for performance reasons
I leave out the details of adding extra checks (answerId and so on) to make it easier to understand.
回答2:
SELECT DISTINCT memberId
FROM members
WHERE answers.questionId=1 AND answers.answerValue IN ('dogs','red')
UNION
SELECT DISTINCT memberId
FROM members
WHERE answers.questionId=2 AND answers.answerValue IN ('cats,'green');
来源:https://stackoverflow.com/questions/9833056/sql-conditioning-multiple-rows-on-an-inner-joined-table