1

Let's say :

SELECT Item.Id, Item.ParentId FROM Item ..." 

Returns me this data:

Id |  ParentId
----------------
1  | NULL
2  | 17
3  | 13

Is there is a way to get this data as one column by using some kind of UNION but on columns from only one SELECT ? Something like:

SELECT (Item.Id UNION Item.ParentId) AS Id FROM Item...

Result :

Id |
----
1  | 
2  |
3  |
NULL
17 |
13 |

EDIT EXAMPLE:

I have Media Table:

Id |  ParentId
----------------
1  | NULL
2  | 1
3  | 2

It have relations with itself, this is some kind of 3 level tree structure (Series -> Seasons -> Episodes)

There is another Table Offer which contain information about availability:

Id |  MediaId  | Availability
------------------------------
1  | 3         | true

I need to get id's of all media that are available, but also all parent's id, of all levels.

I was thinking about:

SELECT Media.Id, MediaSeason.Id, MediaSeries.Id  FROM Media
LEFT JOIN Media AS MediaSeason ON MediaSeason.Id = Media.ParentId
LEFT JOIN Media AS MediaSeries ON MediaSeries.Id = MediaSeason.ParentId 
LEFT JOIN Offer ON Offer.MediaId = Media.Id
WHERE Offer.Availability = true

This gives me all id's i need but in three different columns and I'm trying to find a way to put it into one, without repeating join and where login in 3 different SELECTS.

I'm using MSSQL.

Nick.Mc
  • 18,304
  • 6
  • 61
  • 91
Tomasz Kaniewski
  • 1,065
  • 8
  • 16
  • 2
    Which dialect of SQL are you using? Which RDBMS? MySQL, SQL Server, PostgreSQL, SQLite, Oracle, etc, etc? They all have different tools to do this. But, also, why don't you want to union two selects? Do you believe their is something "bad" about that (you'd be wrong), or are you actually experiencing issues in doing that? – MatBailie Feb 03 '19 at 22:12
  • I have Table (Media) that have relation to itself. This is 3 level relation, so let's say the data can be episode(lvl1), season(lvl2), series(lvl3), all of this is in one table. There is another table let's call it "Offer" which contain information about avaibility. Offer has relation to Media Table. I have to find Id's of all Media that is available, with all level paren't id's in one column. I was thinking of joining 3 media tables on Parent id, join offer to check avaibility and then put all of it in one column – Tomasz Kaniewski Feb 03 '19 at 22:23
  • Please read this, and then update your question with examples : https://stackoverflow.com/help/mcve – MatBailie Feb 03 '19 at 22:26
  • If the table refers to itself, any value in the parent_id column must exist in the id column. So, just `select id from media` gives all the unique ids... – MatBailie Feb 04 '19 at 00:19
  • There might me situations where is an offer on episode, but no offer on it parent season or parent series but still i have to get this 3 Id's, so i can't just select id's... – Tomasz Kaniewski Feb 04 '19 at 00:29
  • If there is an offer on a season, but no individual episode, nor the series, what should be returned? At present you’re not being very clear about what should be returned in different situations. That’s why the link I gave you suggests a full set of example input data, along with the exact results you need for that example data. – MatBailie Feb 04 '19 at 00:31
  • If there is offer on episode -> return episode id, season id, series id; Offer on seaqson -> season id, series id; Offer on series -> series id; – Tomasz Kaniewski Feb 04 '19 at 00:40

4 Answers4

5

Try this:

 SELECT * FROM (SELECT Item.Id FROM Item ...
    UNION ALL
    SELECT Item.ParentId FROM Item ...)
MladenB
  • 161
  • 8
  • I know i can use two SELECT's, the question is can I do in in one SELECT – Tomasz Kaniewski Feb 03 '19 at 22:04
  • @TomaszKaniewski This is the correct method, if you don't want to use it you need to articulate why. (Although, the outer select isn't needed, just the inner query UNIONing two selects.) – MatBailie Feb 03 '19 at 22:14
  • Ok, let's say i have very long query with a lot of JOINs and WHEREs. This query contain two coluns with id I need to return in one column. With simple UNION I have to copy whole JOIN and WHERE logic to two SELECT's and this is what I'm trying to avoid – Tomasz Kaniewski Feb 03 '19 at 22:31
  • That's why the very first comment asked the SQL dialect. Because that kind of requirement is dialect specific. – Nick.Mc Feb 03 '19 at 23:29
  • @MatBailie what's the point of doing the UNION before JOIN in my example? I will have all ids and it's duplicated parent ids. Could u show me how do u think it would work with UNION first? – Tomasz Kaniewski Feb 04 '19 at 00:36
2

If your children and parents are in the same table (Item)

SELECT Id FROM Item

Will retrieve all Items, including Parents because parents are also Items.

But if what you want is to not repeat the where clause and have Ids of any matched Media and its associated parents (even if the parent media does not match the where clause) you can try this:

SELECT 
    m.Id
FROM
    Media m INNER JOIN (
        SELECT 
            m2.Id, m2.ParentId
        FROM 
            Media m2
            LEFT JOIN Offer ON Offer.MediaId = m2.Id
        WHERE 
            Offer.Availability = true
    ) tmp ON (tmp.Id = m.Id OR tmp.ParentId = m.Id)

Finally, for three levels:

SELECT 
    m.Id
FROM
    Media m INNER JOIN (
        SELECT 
            m2.Id, m2.ParentId, m3.ParentId AS GrandParentId
        FROM 
            Media m2
            LEFT JOIN Media m3 ON m2.ParentId = m3.Id
            LEFT JOIN Offer ON Offer.MediaId = m2.Id
        WHERE 
            Offer.Availability = true
    ) tmp ON (tmp.Id = m.Id OR tmp.ParentId = m.Id OR tmp.GrandParentId = m.Id)
mnesarco
  • 2,619
  • 23
  • 31
1
SELECT DISTINCT
    pivot_hierarchy.media_id
FROM
    offers  o
LEFT JOIN
    media   m1
        ON m1.id = o.media_id
LEFT JOIN
    media   m2
        ON m2.id = m1.parent_id
OUTER APPLY
(
    SELECT o.media_id
    UNION ALL
    SELECT m1.parent_id WHERE m1.parent_id IS NOT NULL
    UNION ALL
    SELECT m2.parent_id WHERE m2.parent_id IS NOT NULL
)
    AS pivot_hierarchy
WHERE
    o.availability = 'true'

Everything up to the APPLY should be self explanatory. Get the offers, get the parent of that media if it has one, and the parent of that media if it has one.

The APPLY then joins each row on to a function that can return more than one row each. In this case the function returns 1, 2 or 3 rows. Those being the media id, it parent if it has one, and its grand-parent if it has one. To do that, the function unions the three input columns, provided that they’re not null.

This avoids having to join back on to the media table again.

Also, you need a distinct in the select. Otherwise the same series or season id could return multiple times.

MatBailie
  • 83,401
  • 18
  • 103
  • 137
0

Nested selects can be avoided in UNION

create table tab (
  Id int,
  ParentId int
);

insert into tab 
values
(1, NULL),
(2, 17),
(3, 13);

then do

select ID as ID
from tab
union all
select ParentId as ID
from tab

NOTE: DB queries can be conveniently tested live, e.g. http://sqlfiddle.com/#!17/7a3a8/2

Maciej Skorski
  • 2,303
  • 6
  • 14