简体   繁体   中英

How to do a subquery in group_concat

I have the following data model:

`title`

- id
- name

`version`

- id
- name
- title_id

`version_price`

- id
- version_id
- store
- price

And here is an example of the data:

`title`

- id=1, name=titanic
- id=2, name=avatar

`version`

- id=1, name="titanic (dubbed in spanish)", title_id=1
- id=2, name="avatar directors cut", title_id=2
- id=3, name="avatar theatrical", title_id=2

`version_price`

- id=1, version_id=1, store=itunes, price=$4.99
- id=1, version_id=1, store=google, price=$4.99
- id=1, version_id=2, store=itunes, price=$5.99
- id=1, version_id=3, store=itunes, price=$5.99

I want to construct a query that will give me all titles that have a version_price on iTunes but not on Google. How would I do this? Here is what I have so far:

select 
    title.id, title.name, group_concat(distinct store order by store)
from
    version inner join title on version.title_id=title.id inner join version_price on version_price.version_id=version.id 
group by 
    title_id

This gives me a group_concat which shows me what I have:

id  name    group_concat(distinct store order by store) 
1   Titanic Google,iTunes                               
2   Avatar  iTunes                                       

But how would I construct a query to include whether the item is on Google (using a case statement or whatever's needed)

id  name    group_concat(distinct store order by store) on_google 
1   Titanic Google,iTunes                                true
2   Avatar  iTunes                                       false

It would basically be doing a group_concat LIKE '%google%' instead of a normal where clause.

Here's a link for a SQL fiddle of the current query I have: http://sqlfiddle.com/#!9/e52b53/1/0

Use conditional aggregation to determine if the title is in a specified store.

select title.id, title.name, group_concat(distinct version_price.store order by store),
if(count(case when store = 'google' then 1 end) >= 1,'true','false') as on_google
from version 
inner join title on version.title_id=title.id 
inner join version_price on version_price.version_id=version.id
group by title.id, title.name

count(case when store = 'google' then 1 end) >= 1 counts all the rows for a given title after assigning 1 to the rows which have google in them. (Or else they would be assigned null and the count ignores nulls.) Thereafter, the if checks for the count and classifies a title if it has atleast one google store on it.

This will give you the number of version prices not on google, and the number on google. ( COUNT does not count null values.)

SELECT t.id, t.name
    , COUNT(DISTINCT vpNotG.id) > 0 AS onOtherThanGoogle
    , COUNT(DISTINCT vpG.id) > 0 AS onGoogle
FROM title AS t
    INNER JOIN version AS v ON t.id=v.title_id 
    LEFT JOIN version_price AS vpNotG 
       ON v.id=vpNotG.version_id
       AND vpNotG.store <> 'Google'
    LEFT JOIN version_price AS vpG 
       ON v.id=vpG.version_id 
       AND vpG.store = 'Google'
GROUP BY t.id

or for another solution similar to vkp's:

SELECT t.id, t.name
   , COUNT(DISTINCT CASE WHEN store = 'Google' THEN vp.id ELSE NULL END) AS googlePriceCount
   , COUNT(DISTINCT CASE WHEN store = 'iTunes' THEN vp.id ELSE NULL END) AS iTunesPriceCount
   , COUNT(DISTINCT CASE WHEN store <> 'Google' THEN vp.id ELSE NULL END) AS nonGooglePriceCount
FROM title AS t
   INNER JOIN version AS v ON t.id = v.title_id
   INNER JOIN version_price AS vp ON v.id = vp.version_id
GROUP BY t.id

Note: The ELSE NULL can be omitted, because if no ELSE is provided it is implied; but I included for clarity.

http://sqlfiddle.com/#!9/b8706/2

you can just:

SELECT 
    title.id, 
    title.name, 
    group_concat(distinct version_price.store),
    MAX(IF(version_price.store='google',1,0)) on_google
FROM version 
INNER JOIN title 
ON version.title_id=title.id 
INNER JOIN version_price 
ON version_price.version_id=version.id 
GROUP BY title_id;

and add HAVING to the query if need records to be filtered:

SELECT 
    title.id, 
    title.name, 
    group_concat(distinct version_price.store),
    MAX(IF(version_price.store='google',1,0)) on_google
FROM version 
INNER JOIN title 
ON version.title_id=title.id 
INNER JOIN version_price 
ON version_price.version_id=version.id 
GROUP BY title_id
HAVING on_google;

I would do it like below

SELECT
    *
FROM
    title t
INNER JOIN
    version v ON
        v.title_id = t.id
CROSS APPLY (
    SELECT
       *
    FROM
       version_price vp
    WHERE
       vp.store <> 'google'
 ) c ON c.version_id == v.id

Syntax may be just a little off as I can't test it right now, but I believe this is the spirit of what you would want. Cross apply is also a very efficient join which is always helpful!

This might be the most inefficient of the above answers, but the following subquery would work, using a %like% condition:

select *, case when stores like '%google%' then 1 else 0 end on_google 
from (select title.id, title.name, group_concat(distinct store order by store) stores 
from version inner join title on version.title_id=title.id inner join version_price 
on version_price.version_id=version.id group by title_id) x

id  name    stores  on_google
1   Titanic Google,iTunes   1
2   Avatar  iTunes  0

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM