Question

I have a Locations table that uses a hierarchyid column to map cities/regions/countries/continents. The table looks like this:

declare @Locations table (
  LocationNodeID hierarchyid,
  LocationID int,
  LocationName varchar(50)
)

insert into @Locations (LocationNodeID, LocationID, LocationName) values
  (cast('/0/' as hierarchyid), 1, 'World'),
  (cast('/0/1/' as hierarchyid), 2, 'North America'),
  (cast('/0/1/1/' as hierarchyid), 3, 'United States'),
  (cast('/0/1/1/1/' as hierarchyid), 4, 'California'),
  (cast('/0/1/1/1/1/' as hierarchyid), 5, 'Los Angeles'),
  (cast('/0/1/1/1/2/' as hierarchyid), 6, 'San Francisco'),
  (cast('/0/1/1/2/' as hierarchyid), 7, 'Ohio'),
  (cast('/0/1/1/2/1/' as hierarchyid), 8, 'Cleveland'),
  (cast('/0/1/1/2/2/' as hierarchyid), 9, 'Toledo');

I have a second table that maps events to the locations. This table flattens the hierarchy and has one record per level (I inherited it this way). So if an event is in Los Angeles, there are 4 records for the event in this table: Los Angeles, California, United States, North America. The same event can also be held in multiple locations.

declare @EventLocations table (
  EventID int,
  LocationID int
)

insert into @EventLocations (EventID, LocationID) values
  (1, 2),  -- North America
  (1, 3),  -- United States
  (1, 4),  -- California
  (1, 5),  -- Los Angeles (leaf)

  (2, 2),  -- North America
  (2, 3),  -- United States
  (2, 7),  -- Ohio (leaf)

  (3, 2),  -- North America
  (3, 3),  -- United States (leaf)

  (4, 2),  -- North America
  (4, 3),  -- United States
  (4, 4),  -- California (leaf)
  (4, 7),  -- Ohio
  (4, 9);  -- Toledo (leaf)

I’m trying to create a query the selects the records from @EventLocations that I have identified as leaf nodes. These are records, per event, that have no descendants in relation to the @Locations hierarchy. So it’s possible for a location to be a “leaf” in @EventLocations, but have a descendant in @Locations. I've tried the query below but it only pulls out records that are leafs in the @Locations table.

select ep.EventID, p.*, p2.*
from @EventLocations ep
  inner join @Locations p on ep.LocationID = p.LocationID
  left outer join @Locations p2 on p.LocationNodeID = p2.LocationNodeID.GetAncestor(1)
where p2.LocationID is null
order by ep.EventID, ep.LocationID
Was it helpful?

Solution

I think you're looking for something like the query below. If I'm following you properly, you have to look for parents on the same event.

You generally want to provide sample data and schema information, by the way. It makes it easier for us to help, and possible for us to test the code we suggest.

WITH el AS
    (
        SELECT
            e.eventid
            , l.locationid
            , l.locationnodeid
            , l.locationnodeid.GetAncestor(1) parent
        FROM 
            @EventLocations e
        JOIN
            @Locations l
            ON 
                l.locationid = e.locationid
    )
SELECT
    ep.eventid
    , p.*
FROM 
    el
JOIN
    @EventLocations ep
    ON 
        ep.eventid = el.eventid
        AND 
        ep.locationid = el.locationid
JOIN
    @Locations p
    ON 
        p.locationid = ep.locationid
LEFT JOIN
    el e2
    ON 
        el.eventid = e2.eventid
        AND e2.parent = el.locationnodeid
WHERE
    e2.eventid IS NULL

EDIT:

A simpler version using NOT EXISTS rather than a LEFT JOIN to a CTE:

    SELECT
        ep.EventID
        , p.*
    FROM 
        @EventLocations ep
    JOIN
        @Locations p
        ON 
            p.locationid = ep.locationid    
    WHERE
        NOT EXISTS
            (
                SELECT 
                    NULL
                FROM 
                    @EventLocations el2
                JOIN 
                    @Locations l2
                    ON l2.LocationID = el2.LocationID
                WHERE
                    el2.EventID = ep.EventID
                    AND
                    l2.LocationNodeID.GetAncestor(1) = p.locationnodeid
            );
Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top