Paula DiTallo
asked on
Flattening Data: Need to eliminate rows, but keep join.
Hi Techies--
I've flattened the data with the working SQL (in code block) so that the result set looks like:
SiteID FacilityID
35 135;235;35
instead of:
SiteID FacilityID
35 35
35 135
35 235
What I need to do next is dub 135 and 235 as 35, eliminating the other 2 rows retrieved so that I have:
SiteID FacilityID
35 35
Any thoughts on how to achieve this?
I've flattened the data with the working SQL (in code block) so that the result set looks like:
SiteID FacilityID
35 135;235;35
instead of:
SiteID FacilityID
35 35
35 135
35 235
What I need to do next is dub 135 and 235 as 35, eliminating the other 2 rows retrieved so that I have:
SiteID FacilityID
35 35
Any thoughts on how to achieve this?
SELECT s1.SiteID,
STUFF((SELECT ';'+f.Number
FROM Facility f, [Site] s2
WHERE s2.SiteID = f.SiteID
and s2.SiteID = s1.SiteID
ORDER by f.Number
FOR XML PATH('')),1,1,'') AS [Number]
FROM [Site] s1
GROUP BY SiteID
ASKER CERTIFIED SOLUTION
membership
Create a free account to see this answer
Signing up is free and takes 30 seconds. No credit card required.
Two things are not clear
1. what do you mean by dub 135 and 235 as 35. Is it that only the last two digits count?
-> right(f.Number,2)
2. why 'flattening' when afterwards you want it stripped again
or is a result like this possible (taking 1. into account)
SiteID FacilityID
35 35;42
1. what do you mean by dub 135 and 235 as 35. Is it that only the last two digits count?
-> right(f.Number,2)
2. why 'flattening' when afterwards you want it stripped again
or is a result like this possible (taking 1. into account)
SiteID FacilityID
35 35;42
ASKER
Techies--
Thanks so much for your responses. It is a bit of grappler...as this query on the surface seems so simple to develop... until one tries to accomplish what appears to be the illogical!;-) Flattening the data as I did in the code snippet is the truth of what is stored in the tables-- rolled up for reporting purposes.
@jogos-- the code snippet I included is just to illustrate what does work, but not necessarily what will work in this new case--so no worries about my commitment to xmp path, stuff, etc.!:-) You are very close... in that only one value will get returned with your statement:
SELECT s1.SiteID, min(f.Number) as [Number]
FROM [Site] s1
inner join Facility f on s1.SiteID = f.SiteID
GROUP BY s1.SiteID
...but the value that is returned is '135' -- which is one of the multiple, non-core facilities for a given site. I can
see why min(f.number) would be as good a value to capture as any, because I didn't make it clear that
in each case where there is a site with multiple facilities, one site will retain the same value as the SiteID.
To think this through on another level...
Here's what the joined data would actually look like--simplest case.
Site.SiteID Facility.FacilityID Facility.SiteID
35 35 35
35 135 35
35 235 35
So, as the join pics up the multiple facilities for the site, I am trying to retain the value that matches the SiteID while at the same time, rename the next found FacilityID value whatever it (it won't really be 135 or 235 [so a case won't work]) to the matched value, then the next... and so on.
I suppose as I type this, its basically a foreach row within a cursor type processing (how late '80s is that?)--but surely there's a better way!:-)
Thanks so much for your responses. It is a bit of grappler...as this query on the surface seems so simple to develop... until one tries to accomplish what appears to be the illogical!;-) Flattening the data as I did in the code snippet is the truth of what is stored in the tables-- rolled up for reporting purposes.
@jogos-- the code snippet I included is just to illustrate what does work, but not necessarily what will work in this new case--so no worries about my commitment to xmp path, stuff, etc.!:-) You are very close... in that only one value will get returned with your statement:
SELECT s1.SiteID, min(f.Number) as [Number]
FROM [Site] s1
inner join Facility f on s1.SiteID = f.SiteID
GROUP BY s1.SiteID
...but the value that is returned is '135' -- which is one of the multiple, non-core facilities for a given site. I can
see why min(f.number) would be as good a value to capture as any, because I didn't make it clear that
in each case where there is a site with multiple facilities, one site will retain the same value as the SiteID.
To think this through on another level...
Here's what the joined data would actually look like--simplest case.
Site.SiteID Facility.FacilityID Facility.SiteID
35 35 35
35 135 35
35 235 35
So, as the join pics up the multiple facilities for the site, I am trying to retain the value that matches the SiteID while at the same time, rename the next found FacilityID value whatever it (it won't really be 135 or 235 [so a case won't work]) to the matched value, then the next... and so on.
I suppose as I type this, its basically a foreach row within a cursor type processing (how late '80s is that?)--but surely there's a better way!:-)
ASKER
...well I take that back by systematically stating case tests won't work... perhaps i've been too rash!;-)
ASKER
OK, I did try:
SELECT s1.SiteID,
f.Number as [Number]
FROM [Site] s1
inner join Facility f on s1.SiteID = f.SiteID
WHERE s1.SiteID = f.[Number]
ORDER BY s1.SiteID
This solves my troubles on multiples and created a whole host of new troubles where I have sites without assigned facilities... but its closer!
Thanks for all of your help.
ASKER
Although this isn't exactly the solution--it is close ...especially for anyone coming behind me seeking a way to filter out multiple returns by the minimum value found.
Showing how interpretations of a question can go other directions
Just returning one value
- case when -> when it matches siteId-> keep siteID else NULL
- isnull -> when not siteId is found then take another
Multiple rows when not siteId found
Your rollup but with the siteId-predicate
A little warning about the rollup (or other select/function) in the SELECT. It is executed for every record so it's not so good for performance. Remember that when you write on large numbers of records or want to do that for multiple columns.
Just returning one value
isnull(min(Case when f.number =s1.SiteID then s1.SiteId else null end) , min(f.Number))
What do we do?- case when -> when it matches siteId-> keep siteID else NULL
- isnull -> when not siteId is found then take another
Multiple rows when not siteId found
-- WHERE matches
SELECT s1.SiteID,
f.Number as [Number]
FROM [Site] s1
inner join Facility f on s1.SiteID = f.SiteID
and s1.SiteID = f.[Number]
UNION
-- where no match exists
SELECT s1.SiteID,
f.Number as [Number]
FROM [Site] s1
and not exists (select 1from Facility f
where s1.SiteID = f.SiteID
and s1.SiteID = f.[Number] )
Your rollup but with the siteId-predicate
SELECT s1.SiteID,
isnull(f.Number as [Number], put your rollup here )
FROM [Site] s1
left join Facility f on s1.SiteID = f.SiteID
and s1.SiteID = f.[Number]
Left join on facility with matching -> when not found then the isnull activates your rollup A little warning about the rollup (or other select/function) in the SELECT. It is executed for every record so it's not so good for performance. Remember that when you write on large numbers of records or want to do that for multiple columns.
;with cteFacility AS (SELECT DISTINCT f.FacilityID, CASE WHEN F.SiteID IN (135,235) THEN 35 ELSE F.SiteID END As SiteID FROM Facility F)
SELECT s1.SiteID,
STUFF((SELECT ';'+f.Number
FROM cteFacility f, [Site] s2
WHERE s2.SiteID = f.SiteID
and s2.SiteID = s1.SiteID
ORDER by f.Number
FOR XML PATH('')),1,1,'') AS [Number]
FROM [Site] s1
GROUP BY SiteID