Related
I am trying to only return one email address for each employee. An Employee can be both an employee and a student. If you have both an employee and student email address then I only want to return the employee email address else if you only have student email address then return the student email address.
Here is the entire query:
select --spriden_pidm as pidm,
spriden_id as ban_id,
spriden_last_name as lastname,
spriden_first_name as firstname,
gmal.email,
phone_number.area || phone_number.phone as phone_number,
addr.permanent_address AS street,
addr.permanent_city AS city,
addr.permanent_state AS state,
addr.permanent_zip AS zip,
case
when nbrjobs_ecls_code in ('E1', 'E2', 'EN', 'F1', 'F2') and nbrjobs_ann_salary between 0 and 49999.99 then 'EHRA1'
when nbrjobs_ecls_code in ('E1', 'E2', 'EN', 'F1', 'F2') and nbrjobs_ann_salary between 50000 and 99999.99 then 'EHRA2'
when nbrjobs_ecls_code in ('E1', 'E2', 'EN', 'F1', 'F2') and nbrjobs_ann_salary between 100000 and 149999.99 then 'EHRA3'
when nbrjobs_ecls_code in ('E1', 'E2', 'EN', 'F1', 'F2') and nbrjobs_ann_salary >= 150000 then 'EHRA4'
when nbrjobs_ecls_code in ('SE', 'SN', 'LE') and nbrjobs_ann_salary between 0 and 49999.99 then 'SHRA1'
when nbrjobs_ecls_code in ('SE', 'SN', 'LE') and nbrjobs_ann_salary between 50000 and 99999.99 then 'SHRA2'
when nbrjobs_ecls_code in ('SE', 'SN', 'LE') and nbrjobs_ann_salary between 100000 and 149999.99 then 'SHRA3'
when nbrjobs_ecls_code in ('SE', 'SN', 'LE') and nbrjobs_ann_salary >= 150000 then 'SHRA4'
when nbrjobs_ecls_code in ('FA') then 'AF'
when nbrjobs_ecls_code in ('SH', 'SS', 'TS', 'WS') then 'M1'
else
null
end as empl_cat
from nbrjobs a,
spriden,
(select goremal_pidm as pidm,
goremal_email_address as email
from goremal
where goremal_emal_code in ('EMPL', 'STDN')
and goremal_status_ind = 'A') gmal,
(SELECT sprtele_pidm AS pidm,
sprtele_phone_area AS area,
sprtele_phone_number AS phone
FROM sprtele c
WHERE sprtele_tele_code = 'CA'
AND sprtele_primary_ind = 'Y'
AND sprtele_status_ind IS NULL
AND sprtele_seqno =
(SELECT MAX (sprtele_seqno)
FROM sprtele
WHERE sprtele_tele_code = 'CA'
AND sprtele_primary_ind = 'Y'
AND sprtele_status_ind IS NULL
AND sprtele_pidm = c.sprtele_pidm)) phone_number,
--spraddr
(SELECT spraddr_pidm AS pidm,
spraddr_street_line1 AS permanent_address,
spraddr_city AS permanent_city,
spraddr_stat_code AS permanent_state,
spraddr_zip AS permanent_zip
FROM spraddr b
WHERE spraddr_atyp_code = 'CA'
AND spraddr_status_ind IS NULL
AND spraddr_seqno =
(SELECT MAX (spraddr_seqno)
FROM spraddr
WHERE spraddr_atyp_code = 'CA'
AND spraddr_status_ind IS NULL
AND spraddr_pidm = b.spraddr_pidm)) addr
where a.nbrjobs_pidm = spriden_pidm
and a.nbrjobs_pidm = gmal.pidm(+)
and a.nbrjobs_pidm = phone_number.pidm(+)
and a.nbrjobs_pidm = addr.pidm(+)
and spriden_change_ind is null
and a.nbrjobs_sgrp_code = to_char(sysdate, 'YYYY')
and a.nbrjobs_effective_date = (select max(b.nbrjobs_effective_date)
from nbrjobs b
where b.nbrjobs_pidm = a.nbrjobs_pidm
and b.nbrjobs_posn = a.nbrjobs_posn
and b.nbrjobs_effective_date <= sysdate
--and b.nbrjobs_ecls_code in ('E1','E2','EN','F1','F2','SE','SN','LE')
and b.nbrjobs_ecls_code in ('E1','E2','EN','F1','F2','SE','SN','LE', 'RF', 'AF', 'FA', 'SH', 'SS', 'TS', 'WS')
and b.nbrjobs_sgrp_code = to_char(sysdate, 'YYYY'))
and a.nbrjobs_status <> 'T';`
and this is the part of the query I am trying to change to return the desired email address
(select goremal_pidm as pidm,
goremal_email_address as email
from goremal
where goremal_emal_code in ('EMPL', 'STDN')
and goremal_status_ind = 'A') gmal,
So the issue is that the query will return two email addresses if the employee is also a student? What you can do in this case is PIVOT the data, then use COALESCE() to get the student email where the employee email is NULL. The below query would replace the problematic subquery:
SELECT pidm, COALESCE(empl_email, stdn_email) AS email
FROM (
SELECT goremal_pidm AS pidm, goremal_email_address AS email, goremal_emal_code
FROM goremal
WHERE goremal_emal_code in ('EMPL', 'STDN')
AND goremal_status_ind = 'A'
) PIVOT (
MAX(email) FOR goremal_emal_code IN ('EMPL' AS empl_email, 'STDN' AS stdn_email)
)
EDIT: As an aside, you can use conditional aggregation instead of an explicit PIVOT (helpful if you're using Oracle 9i or lower):
SELECT pidm, COALESCE(empl_email, stdn_email) AS email FROM (
SELECT goremal_pidm AS pidm
, MAX(CASE WHEN goremal_emal_code = 'EMPL' THEN goremal_email_address END) AS empl_email
, MAX(CASE WHEN goremal_emal_code = 'STDN' THEN goremal_email_address END) AS stdn_email
FROM goremal
WHERE goremal_emal_code in ('EMPL', 'STDN')
AND goremal_status_ind = 'A'
GROUP BY goremal_pidm
)
Hope this helps.
Try using NVL2, as a example for your case -
NVL2(EMP_EMAIL_ADR, EMP_EMAIL_ADR, STDN_EMAIL_ADR)
This clause will return if the Employee email address is not null else it returns Student email address.
Hope this helps.
I am using sql server 2014 and I have a table like this (##tt_allresults)
ID | Area | Event |
1 | FB1 | Dev_Chg, Old Value: 0, New Value: 50, Workstation: Blah1, Function: Blah1 func |
1 | FB2 | Dev_Chg, Old Value: 99, New Value: 5, Workstation: Blah2, Function: Blah2 func |
1 | FB1 | Dev_Chg, Old Value: 50, New Value: 55, Workstation: Blah1, Function: Blah1 func |
I would like to from a table like so (Expected Output)
Area | Old Value | New Value | Function |
FB1 | 0 | 50 | Blah1 func |
FB2 | 99 | 5 | Blah2 func |
FB1 | 50 | 55 | Blah1 func |
This is what i have tried so far
Declare #id int
WHILE EXISTS(SELECT * FROM ##tt_allresults)
BEGIN
Select Top 1 #id = Id from ##tt_allresults
-- Do the work --
Declare #area nvarchar(100)
set #area = (Select Area from ##tt_allresults where id = #id)
Insert into ##tt_changedetails
select #area, * from fnsplit((Select [event] from ##tt_allresults where id = #id),',')
-- Scrap the ID and Move On --
Delete ##tt_allresults where ID = #id
END
select * from ##tt_changedetails
I get the following result
Area | ChangeDetails |
FB1 | Dev_Chg |
FB1 | Old value :0 |
FB1 | New Value :50 |
FB1 | Workstation :blah1 |
FB1 | Function :blah1 func |
FB2 | Dev_Chg |
FB2 | Old value :99 |
FB2 | New Value :5 |
FB2 | Workstation :blah2 |
FB2 | Function :blah2 func |
FB1 | Dev_Chg |
FB1 | Old value :50 |
FB1 | New Value :55 |
FB1 | Workstation :blah1 |
FB1 | Function :blah1 func |
How do i split my initial table and pivot it based on the split. I want to see the following result
FB1 | 0 | 50 | Blah1 func |
FB2 | 99 | 5 | Blah2 func |
FB1 | 50 | 55 | Blah1 func |
No need for a UDF, can all be done with the help of a CROSS APPLY and an a little XML
You can expand or contract as needed. I left 9 Positions to illustrate
1) Without a Function
Declare #YourTable table (ID int,Area varchar(25),Event varchar(500))
Insert Into #YourTable values
(1,'FB1','Dev_Chg, Old Value: 0, New Value: 50, Workstation: Blah1, Function: Blah1 func'),
(1,'FB2','Dev_Chg, Old Value: 99, New Value: 5, Workstation: Blah2, Function: Blah2 func'),
(1,'FB1','Dev_Chg, Old Value: 50, New Value: 55, Workstation: Blah1, Function: Blah1 func')
Select A.Area
,[Old Value] = Substring(Pos2,CharIndex(':',Pos2)+1,Len(Pos2))
,[New Value] = Substring(Pos3,CharIndex(':',Pos3)+1,Len(Pos3))
,[Function] = Substring(Pos5,CharIndex(':',Pos5)+1,Len(Pos5))
From #YourTable A
Cross Apply (
Select Pos1 = ltrim(rtrim(xDim.value('/x[1]','varchar(max)')))
,Pos2 = ltrim(rtrim(xDim.value('/x[2]','varchar(max)')))
,Pos3 = ltrim(rtrim(xDim.value('/x[3]','varchar(max)')))
,Pos4 = ltrim(rtrim(xDim.value('/x[4]','varchar(max)')))
,Pos5 = ltrim(rtrim(xDim.value('/x[5]','varchar(max)')))
,Pos6 = ltrim(rtrim(xDim.value('/x[6]','varchar(max)')))
,Pos7 = ltrim(rtrim(xDim.value('/x[7]','varchar(max)')))
,Pos8 = ltrim(rtrim(xDim.value('/x[8]','varchar(max)')))
,Pos9 = ltrim(rtrim(xDim.value('/x[9]','varchar(max)')))
From (Select Cast('<x>' + replace((Select A.Event as [*] For XML Path('')),',','</x><x>')+'</x>' as xml) as xDim) as A
) B
Returns
Area Old Value New Value Function
FB1 0 50 Blah1 func
FB2 99 5 Blah2 func
FB1 50 55 Blah1 func
Or 2 With A Function
Select A.Area
,[Old Value] = Substring(Pos2,CharIndex(':',Pos2)+1,Len(Pos2))
,[New Value] = Substring(Pos3,CharIndex(':',Pos3)+1,Len(Pos3))
,[Function] = Substring(Pos5,CharIndex(':',Pos5)+1,Len(Pos5))
From #YourTable A
Cross Apply [dbo].[udf-Str-Parse-Row](A.Event,',') B
The UDF if needed
ALTER FUNCTION [dbo].[udf-Str-Parse-Row] (#String varchar(max),#Delimiter varchar(10))
Returns Table
As
Return (
Select Pos1 = ltrim(rtrim(xDim.value('/x[1]','varchar(max)')))
,Pos2 = ltrim(rtrim(xDim.value('/x[2]','varchar(max)')))
,Pos3 = ltrim(rtrim(xDim.value('/x[3]','varchar(max)')))
,Pos4 = ltrim(rtrim(xDim.value('/x[4]','varchar(max)')))
,Pos5 = ltrim(rtrim(xDim.value('/x[5]','varchar(max)')))
,Pos6 = ltrim(rtrim(xDim.value('/x[6]','varchar(max)')))
,Pos7 = ltrim(rtrim(xDim.value('/x[7]','varchar(max)')))
,Pos8 = ltrim(rtrim(xDim.value('/x[8]','varchar(max)')))
,Pos9 = ltrim(rtrim(xDim.value('/x[9]','varchar(max)')))
From (Select Cast('<x>' + replace((Select #String as [*] For XML Path('')),#Delimiter,'</x><x>')+'</x>' as xml) as xDim) as A
)
--Select * from [dbo].[udf-Str-Parse-Row]('Dog,Cat,House,Car',',')
--Select * from [dbo].[udf-Str-Parse-Row]('John <test> Cappelletti',' ')
If it helps with the visualization, the CROSS APPLY (which can easily be a TVF) produces the following
here is the correct query I hope it helps .
declare #str varchar(1000)
declare #temp as table (id int , area varchar(10) , [event] varchar(100))
insert into #temp (id,area,[event]) values (1,'FB1','Dev_Chg, Old Value: 0, New Value: 50, Workstation: Blah1, Function: Blah1 func')
insert into #temp (id,area,[event]) values (1,'FB2','Dev_Chg, Old Value: 99, New Value: 5, Workstation: Blah2, Function: Blah2 func')
insert into #temp (id,area,[event]) values (1,'FB1','Dev_Chg, Old Value: 50, New Value: 55, Workstation: Blah1, Function: Blah1 func')
set #str ='Dev_Chg, Old Value: 0, New Value: 50, Workstation: Blah1, Function: Blah1 func'
select * from (
select area, RTRIM( LTRIM( SUBSTRING( String,0 , CHARINDEX (':',String ))) )as theader , SUBSTRING( String, CHARINDEX (':',String )+1,15) as tvalue from (
select * from
#temp cross apply
dbo.ufn_CSVToTable ([event])
) b
where b.String!='Dev_Chg'
)
as final
pivot ( max ( tvalue ) for theader in ([Old Value] , [New Value],[Workstation],[Function])
) as pvt
-- I used a tabled valued function to complete it the source is this
ALTER FUNCTION [dbo].[ufn_CSVToTable] ( #StringInput VARCHAR(8000) )
RETURNS #OutputTable TABLE ( [String] nVARCHAR(1000) )
AS
BEGIN
DECLARE #String nVARCHAR(1000)
WHILE LEN(#StringInput) > 0
BEGIN
SET #String = LEFT(#StringInput,
ISNULL(NULLIF(CHARINDEX(',', #StringInput) - 1, -1),
LEN(#StringInput)))
SET #StringInput = SUBSTRING(#StringInput,
ISNULL(NULLIF(CHARINDEX(',', #StringInput), 0),
LEN(#StringInput)) + 1, LEN(#StringInput))
INSERT INTO #OutputTable ( [String] )
VALUES ( #String )
END
RETURN
END
I am attempting to build a dynamic query based on a list of tables as received from a user.
I have a couple attempted solutions below.
I think the CTE is the way to go but am having difficulty figuring out how to make it happen.
I would really appreciate whatever genius can let me know how to do this!
these are the tables:
W, WD, WE, WSF, WSFE, XDF, XDFE, Y, YD, Z, ZD
these are the columns to join by for each group of tables:
W, Y, WD, WE, WSF
WID
WSF, WSFE, XDF
WSFID
XDF, XDFE
XDFID
Y, YD, Z
YID
Z, ZD
ZID
if the user selects W, Y, Z then build this query (which could then be executed by exec or sp_executesql):
select * from #W w join #Y y on y.WID = w.WID join #Z z on z.YID = y.YID
declare #Fields table (
ID int identity not NULL,
Name varchar(200)
)
declare #Tables table (
ID int identity not NULL,
Field varchar(200),
TempTable varchar(200)
)
declare #QueryTables table (
ID int identity not NULL,
[Table] varchar(200),
Alias varchar(20)
)
declare #QueryJoins table (
ID int identity not NULL,
Table1 varchar(20),
Col1 varchar(200),
Table2 varchar(20),
Col2 varchar(200)
)
insert #Fields
values
('W'),
('Y'),
('Z')
insert #Tables
values
('W', '#W'),
('WD', '#WD'),
('WE', '#WE'),
('WSF', '#WSF'),
('WSFE', '#WSFE'),
('XDF', '#XDF'),
('XDFE', '#XDFE'),
('Y', '#Y'),
('YD', '#YD'),
('Z', '#Z'),
('ZD', '#ZD')
insert #QueryTables
values
('#W', 'w'),
('#WD', 'wd'),
('#WE', 'we'),
('#WSF', 'wsf'),
('#WSFE', 'wsfe'),
('#XDF', 'xdf'),
('#XDFE', 'xdfe'),
('#Y', 'y'),
('#YD', 'yd'),
('#Z', 'z'),
('#ZD', 'zd')
insert #QueryJoins
values
('w', 'WID', 'wd', 'WID'),
('w', 'WID', 'we', 'WID'),
('w', 'WID', 'wsf', 'WID'),
('w', 'WID', 'xdf', 'WID'),
('w', 'WID', 'y', 'WID'),
('wd', 'WID', 'w', 'WID'),
('we', 'WID', 'wd', 'WID'),
('wsf', 'WID', 'wd', 'WID'),
('wsf', 'WSFID', 'wsfe', 'WSFID'),
('wsfe', 'WSFID', 'wsf', 'WSFID'),
('wsf', 'WSFID', 'xdf', 'WSFID'),
('xdf', 'WID', 'w', 'WID'),
('xdf', 'WSFID', 'wsf', 'WSFID'),
('xdf', 'XDFID', 'xdfe', 'XDFID'),
('xdfe', 'XDFID', 'xdf', 'XDFID'),
('y', 'WID', 'w', 'WID'),
('y', 'YID', 'yd', 'YID'),
('yd', 'YID', 'y', 'YID'),
('y', 'YID', 'z', 'YID'),
('z', 'YID', 'y', 'YID'),
('z', 'ZID', 'zd', 'ZID'),
('zd', 'ZID', 'z', 'ZID')
--attempted solution number 1:
select
*
from #Fields vf
join #Tables vt
on vt.Field = vf.Name
join #QueryTables vqt
on vqt.[Table] = vt.TempTable
join #QueryJoins vqj
on vqj.Table1 = vqt.Alias
join #QueryTables vqt2
on vqt2.Alias = vqj.Table2
join #Tables vt2
on vt2.TempTable = vqt2.[Table]
join #Fields vf2
on vf2.Name = vt2.Field
--attempted solution number 2:
;with cte (FieldID, [Table], Table1, Col1, Table2, Col2, I) as (
select
vf.ID as FieldID,
vqt.[Table],
vqj.Table1,
vqj.Col1,
vqj.Table2,
vqj.Col2,
1
from #Fields vf
join #Tables vt
on vt.Field = vf.Name
join #QueryTables vqt
on vqt.[Table] = vt.TempTable
join #QueryJoins vqj
on vqj.Table1 = vqt.Alias
union all
select
vf.ID as FieldID,
vqt.[Table],
vqj.Table1,
vqj.Col1,
vqj.Table2,
vqj.Col2,
I + 1
from #Fields vf
join #Tables vt
on vt.Field = vf.Name
join #QueryTables vqt
on vqt.[Table] = vt.TempTable
join #QueryJoins vqj
on vqj.Table1 = vqt.Alias
join cte cte
on cte.Table1 = vqj.Table2
and cte.Table2 = vqj.Table1
where I <= FieldID --a feeble attempt to control the recursion
)
select * from cte
An interesting problem, definitely a data modeling code smell (if Z, Y, and YD all have a YID column, sounds like you need a bridge table or similar construct to manage the relationships there. Or if Z and YD are small lookups, just join them all and let the optimizer handle the overhead.)
Note: I made the fields self join on Name < Name, so you only need the QueryJoins data where Table1 < Table2. This would cut your QueryJoins table in half, but you're also missing some of the "correct" joins (ie you have we/wd but not wd/we)
declare #Fields table (
ID int identity not NULL,
Name varchar(200)
)
declare #Tables table (
ID int identity not NULL,
Field varchar(200),
TempTable varchar(200)
)
declare #QueryTables table (
ID int identity not NULL,
[Table] varchar(200),
Alias varchar(20)
)
declare #QueryJoins table (
ID int identity not NULL,
Table1 varchar(20),
Col1 varchar(200),
Table2 varchar(20),
Col2 varchar(200)
)
insert #Fields
values
('W'),
('Y'),
('Z')
insert #Tables
values
('W', '#W'),
('WD', '#WD'),
('WE', '#WE'),
('WSF', '#WSF'),
('WSFE', '#WSFE'),
('XDF', '#XDF'),
('XDFE', '#XDFE'),
('Y', '#Y'),
('YD', '#YD'),
('Z', '#Z'),
('ZD', '#ZD')
insert #QueryTables
values
('#W', 'w'),
('#WD', 'wd'),
('#WE', 'we'),
('#WSF', 'wsf'),
('#WSFE', 'wsfe'),
('#XDF', 'xdf'),
('#XDFE', 'xdfe'),
('#Y', 'y'),
('#YD', 'yd'),
('#Z', 'z'),
('#ZD', 'zd')
insert #QueryJoins
values
('w', 'WID', 'wd', 'WID'),
('w', 'WID', 'we', 'WID'),
('w', 'WID', 'wsf', 'WID'),
('w', 'WID', 'xdf', 'WID'),
('w', 'WID', 'y', 'WID'),
('wd', 'WID', 'w', 'WID'),
('we', 'WID', 'wd', 'WID'),
('wsf', 'WID', 'wd', 'WID'),
('wsf', 'WSFID', 'wsfe', 'WSFID'),
('wsfe', 'WSFID', 'wsf', 'WSFID'),
('wsf', 'WSFID', 'xdf', 'WSFID'),
('xdf', 'WID', 'w', 'WID'),
('xdf', 'WSFID', 'wsf', 'WSFID'),
('xdf', 'XDFID', 'xdfe', 'XDFID'),
('xdfe', 'XDFID', 'xdf', 'XDFID'),
('y', 'YID', 'yd', 'YID'),
('yd', 'YID', 'y', 'YID'),
('y', 'YID', 'z', 'YID'),
('z', 'ZID', 'zd', 'ZID'),
('zd', 'ZID', 'z', 'ZID')
;
with a as (
select
row_number() over (order by Name) as rn, Name, Field, TempTable, [Table], Alias
from #Fields vf
join #Tables vt
on vt.Field = vf.Name
join #QueryTables vqt
on vqt.[Table] = vt.TempTable )
select 'select * from ' + stuff((
select
concat(
case when a.rn =1 then a.TempTable else '' end,
' ',
case when a.rn =1 then a.Alias else '' end,
' join ',
a2.TempTable,
' ' ,
a2.alias,
' on ',
q.Table1,
'.',
q.Col1,
' = ',
q.Table2 ,
'.',
q.Col2 ) from a
left join a a2
on a.name < a2.name
inner join #QueryJoins q
on q.Table1 = a.alias
and q.Table2 = a2.alias
for xml path('')), 1, 1, '')
WHERE (
((SERVICECOMPONENT_ID IN (123, 150, 198, 199, 290, 287, 291, 289, 288, 286, 282, 281)))
OR ((SERVICEREQUEST_ID IN (
SELECT distinct(SR.SERVICEREQUEST_ID)
FROM SERVICE_REQUEST SR,ASSIGNED_SR_PROJECTS ASP,PROJECT_RESOURCES PRS
WHERE SR.SERVICEREQUEST_ID = ASP.SERVICEREQUEST_ID
AND PRS.PROJECT_ID = ASP.PROJECT_ID
AND PRS.RESPONSIBILITY IN ('MANAGER','LEAD')
AND PRS.RESOURCE_ID =180 )) )
)
In general,
SELECT a FROM b WHERE c IN (SELECT d FROM e)
is equivalent to
SELECT a FROM b WHERE EXISTS (SELECT 1 FROM e WHERE c = d)
The SERVICEREQUEST_ID IN (subquery) part of your code example translates to:
OR EXISTS (
SELECT 1
FROM
SERVICE_REQUEST SR,
ASSIGNED_SR_PROJECTS ASP,
PROJECT_RESOURCES PRS
WHERE
SR.SERVICEREQUEST_ID = ASP.SERVICEREQUEST_ID
AND PRS.PROJECT_ID = ASP.PROJECT_ID
AND PRS.RESPONSIBILITY IN ('MANAGER', 'LEAD')
AND PRS.RESOURCE_ID = 180
AND mytable.SERVICEREQUEST_ID = SR.SERVICEREQUEST_ID
)
if you have static list of elements It's better to use "IN".... If you have a subquery and it is returning more than one value then Use Exist...
There is no Difference in Both clauses..
WHERE (
( (SERVICECOMPONENT_ID IN ( 123 , 150 , 198 , 199 , 290 , 287 , 291 , 289 , 288 , 286 , 282 , 281 )) )
OR ( (SERVICEREQUEST_ID IN ( 1952 , 2387 , 3618 , 3633 , 4178 , 4432 , 5090 , 5271 , 6068 , 6320 , 6396 , 6526 , 7162 , 7442 , 7558 , 7639 , 7688 , 8176 , 8189 , 8338 , 8460 , 8461 , 8598 , 8612 , 8628 , 8675 , 8775 , 8869 , 8886 , 8898 )) )
OR ( (REQUESTED_BY LIKE 'XXXXXXX#example.com' ) )
OR ( ( EXISTS ( SELECT count(distinct(SR.SERVICEREQUEST_ID)) FROM SERVICE_REQUEST SR,ASSIGNED_SR_PROJECTS ASP,PROJECT_RESOURCES PRS WHERE SR.SERVICEREQUEST_ID = ASP.SERVICEREQUEST_ID AND PRS.PROJECT_ID = ASP.PROJECT_ID AND PRS.RESPONSIBILITY IN ('MANAGER','LEAD') AND PRS.RESOURCE_ID =180 )) )
OR ( (STATUS_CODE LIKE 'OPEN' ) AND ( EXISTS (SELECT count(COMPONENT.CATEGORY_ID) FROM PROJECTMASTER PROJECTS, BUDGET BUDGET, CONTRACT CONTRACT,COMPONENTS COMPONENT, PROJECT_RESOURCES PROJ_RESOURCES, CATEGORY_OWNER_ASSIGNMENT CATEGORYOWNER, SERVICECATEGORYASSIGNMENT CATEGORYASSIGNMENT WHERE PROJECTS.PROJECT_ID = PROJ_RESOURCES.PROJECT_ID AND PROJECTS.BUDGET_ID = BUDGET.BUDGET_ID AND BUDGET.CONTRACT = CONTRACT.CONTRACT_ID AND CATEGORYASSIGNMENT.CONTRACT_ID = CONTRACT.CONTRACT_ID AND COMPONENT.COMPONENT_ID = CATEGORYASSIGNMENT.COMPONENT_ID AND CATEGORYOWNER.CATEGORY_ID = COMPONENT.CATEGORY_ID AND CATEGORYOWNER.USER_ID = PROJ_RESOURCES.RESOURCE_ID AND (CATEGORYOWNER.OWNER_FLAG = 'Y' OR CATEGORYOWNER.MEMBER_FLAG = 'Y') AND PROJ_RESOURCES.RESOURCE_ID = 180 AND PROJ_RESOURCES.ACTIVE_FLAG = 'Y' AND CATEGORYASSIGNMENT.ACTIVE_FLAG = 'Y' AND PROJ_RESOURCES.RESPONSIBILITY IN ('MANAGER', 'LEAD') )) )
)
read this for further clarification..
Difference between EXISTS and IN in SQL?
I'm trying to retrieve data from a (SQL Server 2008 R2) table in XML format and can't seem to get it into an ideal structure. I've tried various FOR XML modes and join methods, but I have minimal experience with XML and have not worked with XML output via SQL prior to this.
My data is in a table named XMLResults with the following structure:
ResultID Country Product Disposition Results
-----------------------------------------------
1 UK ABC Result1 100
2 UK ABC Result2 200
3 UK ABC Result3 300
4 UK XYZ Result1 100
5 UK XYZ Result2 200
6 UK XYZ Result3 300
7 USA ABC Result1 100
8 USA ABC Result2 200
9 USA ABC Result3 300
10 USA XYZ Result1 100
11 USA XYZ Result2 200
12 USA XYZ Result3 300
Currently I have this query:
SELECT (SELECT Country,
(SELECT Product,
(SELECT Disposition, Results
FROM XMLResults t1
WHERE t1.ResultID = t2.ResultID
FOR XML PATH ('Dispositions'), TYPE, ELEMENTS
)
FROM XMLResults t2
WHERE t2.ResultID = t3.ResultID
FOR XML PATH ('Products'), TYPE, ELEMENTS
)
FROM XMLResults t3
ORDER BY Country, Product
FOR XML PATH ('Countries'), TYPE, ELEMENTS
)
FOR XML PATH('Stats');
Which returns the XML like this:
<Stats>
<Countries>
<Country>UK</Country>
<Products>
<Product>ABC</Product>
<Dispositions>
<Disposition>Result1</Disposition>
<Results>100</Results>
</Dispositions>
</Products>
</Countries>
<Countries>
<Country>UK</Country>
<Products>
<Product>ABC</Product>
<Dispositions>
<Disposition>Result2</Disposition>
<Results>200</Results>
</Dispositions>
</Products>
</Countries>
<Countries>
<Country>UK</Country>
<Products>
<Product>ABC</Product>
<Dispositions>
<Disposition>Result3</Disposition>
<Results>300</Results>
</Dispositions>
</Products>
</Countries>
...
</Stats>
Not terrible, but I'd prefer it not drop all the way back out to the 'Countries' level after each result, and probably don't need the extra generic tags either.
Something like this would be better:
<Stats>
<Countries>
<Country = "UK">
<Products>
<Product = "ABC">
<Dispositions>
<Disposition>
<ReasonCode>Result1</ReasonCode>
<Count>100</Count>
</Disposition>
<Disposition>
<ReasonCode>Result2</ReasonCode>
<Count>200</Count>
</Disposition>
<Disposition>
<ReasonCode>Result3</ReasonCode>
<Count>300</Count>
</Disposition>
</Dispositions>
</Product>
...
</Products>
</Country>
...
</Countries>
</Stats>
Maybe even something like this (looks a little bit cleaner):
<Stats>
<Country = "UK">
<Product = "ABC">
<Disposition ReasonCode = "Result1" Count = "100" />
<Disposition ReasonCode = "Result2" Count = "200" />
<Disposition ReasonCode = "Result3" Count = "300" />
</Product>
<Product = "XYZ">
<Disposition ReasonCode = "Result1" Count = "100" />
<Disposition ReasonCode = "Result2" Count = "200" />
<Disposition ReasonCode = "Result3" Count = "300" />
</Product>
</Country>
<Country = "USA">
<Product = "ABC">
<Disposition ReasonCode = "Result1" Count = "100" />
<Disposition ReasonCode = "Result2" Count = "200" />
<Disposition ReasonCode = "Result3" Count = "300" />
</Product>
<Product = "XYZ">
<Disposition ReasonCode = "Result1" Count = "100" />
<Disposition ReasonCode = "Result2" Count = "200" />
<Disposition ReasonCode = "Result3" Count = "300" />
</Product>
</Country>
</Stats>
The output format isn't set in stone, so if there are recommendations, I'm open to suggestions on that as well.
Thanks,
Sean
Edit for sample data:
CREATE TABLE XMLResults (
ResultID BIGINT IDENTITY(1,1) NOT NULL
, Country VARCHAR(50) NOT NULL
, Product VARCHAR(50) NOT NULL
, Disposition VARCHAR(50) NOT NULL
, Results INT NOT NULL);
INSERT INTO XMLResults (Country, Product, Disposition, Results)
VALUES ('UK', 'ABC', 'Result1', 100)
, ('UK', 'ABC', 'Result2', 200)
, ('UK', 'ABC', 'Result3', 300)
, ('UK', 'XYZ', 'Result1', 100)
, ('UK', 'XYZ', 'Result2', 200)
, ('UK', 'XYZ', 'Result3', 300)
, ('USA', 'ABC', 'Result1', 100)
, ('USA', 'ABC', 'Result2', 200)
, ('USA', 'ABC', 'Result3', 300)
, ('USA', 'XYZ', 'Result1', 100)
, ('USA', 'XYZ', 'Result2', 200)
, ('USA', 'XYZ', 'Result3', 300);
If you want attribute-centric xml, prepend '#' to your column aliases. You can completely customize the output, e.g:
SELECT
Country [#Country],
(SELECT
Product [#Product],
(SELECT
Disposition [#ReasonCode],
Results [#Count]
FROM #XMLResults t3
WHERE t3.Country = t1.Country AND t3.Product = t2.Product
FOR XML PATH('Disposition'),TYPE)
FROM #XMLResults t2
WHERE t2.Country = t1.Country
GROUP BY Product
FOR XML PATH('Product'),TYPE)
FROM #XMLResults t1
GROUP BY Country
FOR XML PATH('Country'), ROOT ('Stats')
Just play around with it.