SQL: conditioning multiple rows on an inner joined table

不羁岁月 提交于 2019-12-23 02:04:08

问题


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

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!