nested dependent sql question - sql

I'm trying to implement a front end for a reporting solution which is security dependent. The user has 12 levels of nested criteria to select from, the value of each affects all of the values below.
So the criteria selection on the page (each is a dropdown) looks something like this:
Criteria 1
Criteria 2
...
Criteria 12
There is a Security table that holds the values that are available to each user which has the following structure:
EmployeeID | Criteria_1_valid_Value | C2_valid_Value | ... | C12_valid_Value
x0001 | c1 | c2 | ... | c12
and each Employee will have one or (many) more rows in this table. Think of it as a flattened tree, with Criteria1 as the root node.
Based on keys, changing Criteria 1 will affect the values that are visible in Criteria 2 through 12. In the same way, changing the value in Criteria 2 affects the values available in Criteria 3 through Criteria 12. At each level, there is an option to select 'All Values,' which is represented by a space internally, for lookups. So I need a representation in the lookup table/view which takes into account that there may be a space at one or many levels.
Where I'm struggling is with finding a way to build the lookup view/table for each Criteria field using sql without having to resort to hardcoding.
For example, to build the lookup for criteria 2 the sql might look like this:
select EmployeeID, Criteria1, Criteria2
from Security
Union
select EmployeeID, ' ', Criteria2
from Security
Union
select EmployeeID, Criteria1, ' '
from Security
UNION
select EmployeeID, ' ', ' '
from Security
And so on. Unfortunately, of course, with 12 levels, the last works out to 2^12 unions, which frankly smells.
I've tried building a table for each level in batch, committing after each, then using the previous table joined to the Security table to build the next with a single UNION in each, but I can't seem to get the joins to work properly with the spaces.
I don't know if I'm overthinking this or completely missing something, but I feel like there has to be a simpler solution.
EDIT: This is on Oracle and I'm working with an ERP product as the underlying technology.
EDIT2: Thanks for the input everyone. I got the joins eorking correctly using joins like in the example proc from #Alex Poole below:
and (v_Criteria_1 = ' ' or Criteria_1_valid_Value = v_Criteria_1)
I was missing the v_Criteria_1 = ' ' or.
So I've got the tables loaded correctly (enough) now. This is turning into a tuning/optimization exercise. I'm going to look at the proc from #Alex Poole and the artithmetic approach of #JD_55 which I think might be very quick.

If your security table structure is something like
Name Null? Type
----------------------------------------- -------- ----------------------------
EMPLOYEEID VARCHAR2(9)
CRITERIA_1_VALID_VALUE VARCHAR2(15)
CRITERIA_2_VALID_VALUE VARCHAR2(15)
CRITERIA_3_VALID_VALUE VARCHAR2(15)
with data
EMPLOYEEI CRITERIA_1_VALI CRITERIA_2_VALI CRITERIA_3_VALI
--------- --------------- --------------- ---------------
alex crit 1a crit 1a 2a crit 1a 2a 3a
alex crit 1a crit 1a 2b crit 1a 2b 3a
alex crit 1a crit 1a 2c crit 1a 2c 3a
alex crit 1a crit 1a 2c crit 1a 2c 3b
alex crit 1b crit 1b 2a crit 1b 2a 3a
alex crit 1b crit 1b 2b crit 1b 2b 3a
alex crit 1b crit 1b 2c crit 1b 2c 3a
alex crit 1c crit 1c 2a crit 1c 2a 3a
then does this give the result you need?
create or replace type t_crit_values as table of varchar2(15)
/
show errors
create or replace function get_criteria(v_EmployeeID in varchar2,
v_Level in number,
v_Criteria_1 in varchar2 default ' ',
v_Criteria_2 in varchar2 default ' ',
v_Criteria_3 in varchar2 default ' ')
return t_crit_values as
cursor c_values is
select distinct(case v_Level
when 1 then Criteria_1_valid_Value
when 2 then Criteria_2_valid_Value
when 3 then Criteria_3_valid_Value
end) value
from security
where EmployeeID = v_EmployeeID
and (v_Criteria_1 = ' ' or Criteria_1_valid_Value = v_Criteria_1)
and (v_Criteria_2 = ' ' or Criteria_2_valid_Value = v_Criteria_2)
and (v_Criteria_3 = ' ' or Criteria_3_valid_Value = v_Criteria_3);
l_crit_values t_crit_values;
i number;
begin
l_crit_values := t_crit_values();
for r_value in c_values loop
l_crit_values.EXTEND;
l_crit_values(l_crit_values.LAST) := r_value.value;
end loop;
return l_crit_values;
end;
/
show errors
Then call the function, each time passing in the level you need and the selected values from all higher levels (which may be ' '). Something like
// first level
select * from table(get_criteria('alex', 1));
COLUMN_VALUE
---------------
crit 1a
crit 1b
crit 1c
// second level with 'crit 1b' selected
select * from table(get_criteria('alex', 2, 'crit 1b'));
COLUMN_VALUE
---------------
crit 1b 2a
crit 1b 2b
crit 1b 2c
// second level with 'crit 1c' selected
select * from table(get_criteria('alex', 2, 'crit 1c'));
COLUMN_VALUE
---------------
crit 1c 2a
// third level with 'crit 1b' and 'crit 1b 2a' selected
select * from table(get_criteria('alex', 3, 'crit 1b', 'crit 1b 2a'));
COLUMN_VALUE
---------------
crit 1b 2a 3a
// third level with 'crit 1b' and 'all values' selected
select * from table(get_criteria('alex', 3, 'crit 1b', ' '));
COLUMN_VALUE
---------------
crit 1b 2a 3a
crit 1b 2b 3a
crit 1b 2c 3a
I've only gone to three levels for brevity but it would be easy to expand. Or have I not understood what you're trying to do?

Say there were only 3 criteria you want a table like this?
id c1 c2 c3
0 a b c
1 a b space
2 a space c
3 a space space
4 space b c
5 space b space
6 space space c
7 space space space
If you create such a table using sqlloader for example including an id column from 0 to 2^12 -1 , put spaces into all the criteria columns then you could update it using arithmetic:
update temp set c1 = (select criteria1 ...) where mod(id,2) < 1;
update temp set c2 = (select criteria2 ...) where mod(id,4) < 2;
update temp set c3 = (select criteria3 ...) where mod(id,8) < 4;
Does seem like a weird requirement.

Consider a series of left outer self join with each criteria item dependent on the values of the prior criteria. You can use the NVL() function to return spaces instead of nulls when the left join produces null results:
select a.employeeId,
nvl(c1.criteria_1, ' '),
nvl(c2.criteria_2, ' '),
nvl(c3.criteria_3, ' '),
nvl(c4.criteria_4, ' '),
nvl(c5.criteria_5, ' '),
nvl(c6.criteria_6, ' '),
nvl(c7.criteria_7, ' '),
nvl(c8.criteria_8, ' '),
nvl(c9.criteria_9, ' '),
nvl(c10.criteria_10, ' '),
nvl(c11.criteria_11, ' '),
nvl(c12.criteria_12, ' ')
from security as a,
left outer join security as c1
on (c1.employeeId = a.employeeId)
left outer join security as c2
on (c2.employeeId = a.employeeId and
c2.criteria_1 = a.criteria_1)
left outer join security as c3
on (c3.employeeId = a.employeeId and
c3.criteria_1 = a.criteria_1 and
c3.criteria_2 = a.criteria_2)
left outer join security as c4
on (c4.employeeId = a.employeeId and
c4.criteria_1 = a.criteria_1 and
c4.criteria_2 = a.criteria_2 and
c4.criteria_3 = a.criteria_3)
left outer join security as c5
on (c5.employeeId = a.employeeId and
c5.criteria_1 = c1.criteria_1 and
c5.criteria_2 = a.criteria_2 and
c5.criteria_3 = a.criteria_3 and
c5.criteria_4 = a.criteria_4)
left outer join security as c6
on (c6.employeeId = a.employeeId and
c6.criteria_1 = c1.criteria_1 and
c6.criteria_2 = a.criteria_2 and
c6.criteria_3 = a.criteria_3 and
c6.criteria_4 = a.criteria_4 and
c6.criteria_5 = a.criteria_5)
left outer join security as c7
on (c7.employeeId = a.employeeId and
c7.criteria_1 = c1.criteria_1 and
c7.criteria_2 = a.criteria_2 and
c7.criteria_3 = a.criteria_3 and
c7.criteria_4 = a.criteria_4 and
c7.criteria_5 = a.criteria_5 and
c7.criteria_6 = a.criteria_6)
left outer join security as c8
on (c8.employeeId = a.employeeId and
c8.criteria_1 = c1.criteria_1 and
c8.criteria_2 = a.criteria_2 and
c8.criteria_3 = a.criteria_3 and
c8.criteria_4 = a.criteria_4 and
c8.criteria_5 = a.criteria_5 and
c8.criteria_6 = a.criteria_6 and
c8.criteria_7 = a.criteria_7)
left outer join security as c9
on (c9.employeeId = a.employeeId and
c9.criteria_1 = c1.criteria_1 and
c9.criteria_2 = a.criteria_2 and
c9.criteria_3 = a.criteria_3 and
c9.criteria_4 = a.criteria_4 and
c9.criteria_5 = a.criteria_5 and
c9.criteria_6 = a.criteria_6 and
c9.criteria_7 = a.criteria_7 and
c9.criteria_8 = a.criteria_8)
left outer join security as c10
on (c10.employeeId = a.employeeId and
c10.criteria_1 = c1.criteria_1 and
c10.criteria_2 = a.criteria_2 and
c10.criteria_3 = a.criteria_3 and
c10.criteria_4 = a.criteria_4 and
c10.criteria_5 = a.criteria_5 and
c10.criteria_6 = a.criteria_6 and
c10.criteria_7 = a.criteria_7 and
c10.criteria_8 = a.criteria_8 and
c10.criteria_9 = a.criteria_9)
left outer join security as c11
on (c11.employeeId = a.employeeId and
c11.criteria_1 = c1.criteria_1 and
c11.criteria_2 = a.criteria_2 and
c11.criteria_3 = a.criteria_3 and
c11.criteria_4 = a.criteria_4 and
c11.criteria_5 = a.criteria_5 and
c11.criteria_6 = a.criteria_6 and
c11.criteria_7 = a.criteria_7 and
c11.criteria_8 = a.criteria_8 and
c11.criteria_9 = a.criteria_9 and
c11.criteria_10 = a.criteria_10)
left outer join security as c12
on (c12.employeeId = a.employeeId and
c12.criteria_1 = c1.criteria_1 and
c12.criteria_2 = a.criteria_2 and
c12.criteria_3 = a.criteria_3 and
c12.criteria_4 = a.criteria_4 and
c12.criteria_5 = a.criteria_5 and
c12.criteria_6 = a.criteria_6 and
c12.criteria_7 = a.criteria_7 and
c12.criteria_8 = a.criteria_8 and
c12.criteria_9 = a.criteria_9 and
c12.criteria_10 = a.criteria_10 and
c12.criteria_11 = a.criteria_11);

I don't fully understand your requirements, but I would have thought that the query for criteria2 would be:
select distinct Criteria2
from Security
where EmployeeID = :the_user
and Criteria1 = :Criteria1
if the user must enter Criteria1 before Criteria2, or
select distinct Criteria2
from Security
where EmployeeID = :the_user
and (:Criteria1 is null or Criteria1 = :Criteria1)
otherwise?

So in the end it did come down to a performance issue. I created a table that held the binary representation of 2^10 integers in reverse (litte-endian, if you will).
DECBIN:
decimal binary
0 0000000000
1 1000000000
2 0100000000
...
1023 1111111111
I then cartesian join this to the security table and decode each bit to get the correct value.
So the sql looks something like this:
SELECT DISTINCT
t.employeeID,
DECODE (SUBSTR (x.binary, 1, 1), 0, ' ', t.c1) AS crit1,
DECODE (SUBSTR (x.binary, 2, 1), 0, ' ', t.c2) AS crit2,
DECODE (SUBSTR (x.binary, 3, 1), 0, ' ', t.c3) AS crit3,
DECODE (SUBSTR (x.binary, 4, 1), 0, ' ', t.c4) AS crit4,
DECODE (SUBSTR (x.binary, 5, 1), 0, ' ', t.c5) AS crit5,
DECODE (SUBSTR (x.binary, 6, 1), 0, ' ', t.c6) AS crit6,
DECODE (SUBSTR (x.binary, 7, 1), 0, ' ', t.c7) AS crit7,
DECODE (SUBSTR (x.binary, 8, 1), 0, ' ', t.c8) AS crit8,
DECODE (SUBSTR (x.binary, 9, 1), 0, ' ', t.c9) AS crit9,
DECODE (SUBSTR (x.binary, 10, 1), 0, ' ', t.c10) AS crit10,
DECODE (SUBSTR (x.binary, 10, 1), 0, 'Choose All',t.c11) AS crit10Descr
FROM Security t, DECBIN x
WHERE TO_NUMBER (x.decimal) BETWEEN 0 AND POWER (2, 10) - 1
This is faster by a factor of 10. Thanks #JD_55 for getting me tho think about the problem in a new way.

Related

Including rows with no values in SQL query output

Running the following query in my SQL SERVER 2016 database:
SELECT
u.idnumber,
gi.idnumber AS code,
gg.finalgrade AS grade
FROM
grades AS gg
INNER JOIN grade_items AS gi ON gg.itemid = gi.id
INNER JOIN users AS u ON gg.userid = u.id
WHERE gi.idnumber IN ('436','434','313','002','135')
AND (u.idnumber = 'JohnBrown-xyz123);
gives me this result:
-------------------------
idnumber code grade
-------------------------
John12558 313 96
John12558 135 83
How can I include a row for ALL the 5 gi.idnumber's, including the rows with no grade?
-------------------------
idnumber code grade
-------------------------
John12558 436
John12558 434
John12558 313 96
John12558 002
John12558 135 83
Database tables:
user
id PK
grade_items
id PK
grade_grades
grade_grades.itemid FK (grade_items)
grade_grades.userid FK (user)
You can use value construct like that :
select tt.idnumber,
t.idnumber AS code,
tt.grade
from (values ('436'), ('434'), ('313'), ('002'), ('135')) t (idnumber)
left join (select
u.idnumber,
gi.idnumber AS code,
gg.finalgrade AS grade
from grades AS gg
inner join grade_items AS gi ON gg.itemid = gi.id
inner join users AS u ON gg.userid = u.id
where u.idnumber = 'JohnBrown-xyz123') tt on left(tt.code, 3) = t.idnumber
If, idnumber has numeric type, then you don't need to use ' '.
You need cross join to see all you want:
select
u.idnumber,
gi.idnumber code,
ISNULL(gg.finalgrade, 0) grade
from users u
cross join grade_items gi
left outer join grades gg on gg.itemid = gi.id and gg.userid = u.id
where gi.idnumber in ('436','434','313','002','135')
and u.idnumber = 'JohnBrown-xyz123'
nulls in finalgrade cannot be replaced with 'N/A' because they are numbers, not strings, I replaced them with zero.
Assuming "no value" is NULL then just add an extra clause to your WHERE:
WHERE (LEFT(gi.idnumber, 3) IN ('436','434','313','002','135')
OR gi.idnumber IS NULL)
Edit: Apaprently, the above does not work, which implies that "no value" does not mean NULL. you could, instead, therefore try:
WHERE (LEFT(gi.idnumber, 3) IN ('436','434','313','002','135','')
If this doesn't work, please provide consumable sample data, which we can test against: Forum Etiquette: How to post data for a T-SQL Question

Combining multiple rows in TSQL query

I have a table with data like this
Road Item Response added_on
1 82 Yes 7/11/16
1 83 Yes 7/11/16
1 84 Yes 7/11/16
2 82 Yes 8/11/16
2 83 No 8/11/16
2 85 Yes 8/11/16
This reflects an assessment of a road where 'item' is things being assessed.
Some items will always be done during an assessment (82, 83) where others are optional (84, 85).
I want to return something that combines all of the assessment results for a road/date, returning null if that item was not assessed. And also only returning last month's results. For example
Road 82 83 84 85 added_on
1 Yes Yes Yes 7/11/16
2 Yes No Yes 8/11/16
I have tried a multiple self joins like this but it's returning nothing.
FROM assess AS A
JOIN assess AS B
ON A.road = B.road AND a.added_on = B.added on
JOIN assess AS C
ON A.road = C.road AND a.added_on = C.added on
JOIN assess AS D
ON A.road = D.road AND a.added_on = D.added on
WHERE A.item = '81'
AND B.item = '82'
AND (C.item = '83' OR C.item IS NULL)
AND (D.item = '84' OR D.item IS NULL)
AND datepart(month,A.added_on) = datepart(month,getdate()) -1
To clarify,
-no road is assessed more than once a day
-each item is only assessed once, and sometimes is NULL i.e. not applicable
-multiple roads are assessed each day
-this table has other assessments but we aren't worried about those.
Any ideas? Using SQL server 2008. Thanks.
Assuming you need to go Dynamic
Declare #SQL varchar(max)
Select #SQL = Stuff((Select Distinct ',' + QuoteName(Item) From YourTable Order By 1 For XML Path('')),1,1,'')
Select #SQL = 'Select [Road],' + #SQL + ',[added_on]
From YourTable
Pivot (max(Response) For Item in (' + #SQL + ') ) p'
Exec(#SQL);
Returns
EDIT - The SQL Generated is as follows. (just in case you can't go
dynamic)
Select [Road],[82],[83],[84],[85],[added_on]
From YourTable
Pivot (max(Response) For Item in ([82],[83],[84],[85]) ) p
Another way of achieving this is less elegant, but uses basic operations if you don't want to use pivot.
Load up test data
create table #assess ( road int, item varchar(10), response varchar(3), added_on date )
insert #assess( road, item, response, added_on )
values
(1, '82', 'Yes', '2016-07-11' )
, (1, '83', 'Yes', '2016-07-11' )
, (1, '84', 'Yes', '2016-07-11' )
, (2, '82', 'Yes', '2016-08-11' )
, (2, '83', 'No', '2016-08-11' )
, (2, '85', 'Yes', '2016-08-11' )
Process the data
-- Get every possible `item`
select distinct item into #items from #assess
-- Ensure every road/added_on combination has all possible values of `item`
-- If the combination does not exist in original data, leave `response` as blank
select road, added_on, i.item, cast('' as varchar(3)) as response into #assess2
from #items as i cross join #assess AS A
group by road, added_on, i.item
update a set response = b.response
from #assess2 a inner join #assess b on A.road = B.road AND a.added_on = B.added_on AND a.item = b.item
-- Join table to itself 4 times - inner join if `item` must exist or left join if `item` is optional
select a.road, a.added_on, a.response as '82', b.response as '83', c.response as '84', d.response as '85'
FROM #assess2 AS A
INNER JOIN #assess2 AS B ON A.road = B.road AND a.added_on = B.added_on
LEFT JOIN #assess2 AS C ON A.road = C.road AND a.added_on = C.added_on
LEFT JOIN #assess2 AS D ON A.road = D.road AND a.added_on = D.added_on
WHERE A.item = '82'
AND B.item = '83'
AND (C.item = '84' OR C.item IS NULL)
AND (D.item = '85' OR D.item IS NULL)
--AND datepart(month,A.added_on) = datepart(month,getdate()) -1
The resultset is:
road added_on 82 83 84 85
1 2016-07-11 Yes Yes Yes
2 2016-08-11 Yes No Yes
I would do this using conditional aggregation:
select road,
max(case when item = 82 then response end) as response_82,
max(case when item = 83 then response end) as response_83,
max(case when item = 84 then response end) as response_84,
max(case when item = 85 then response end) as response_85,
added_on
from t
group by road, added_on
order by road;
For the month component, you can add a where clause. One method is:
where year(date_added) * 12 + month(date_added) = year(getdate())*12 + month(getdate()) - 1
Or, you can use logic like this:
where date_added < dateadd(day, 1 - day(getdate()), cast(getdate() as date)) and
date_added >= dateadd(month, -1, dateadd(day, 1 - day(getdate()), cast(getdate() as date)))
The second looks more complicated but it is sargable, meaning that an index on date_added can be used (if one is available).

SQL Random Select No Repeat

I have a set that looks like this:
staffID clientID eventID
s1 c1 e1c1
s1 c1 e2c1
s1 c2 e1c2
s2 c3 e1c3
s2 c4 e1c4
s2 c5 e1c5
s2 c6 e1c6
I want to select from this list two random clients for each staff. The clients should not repeat. This is not an issue for s2 but can be a problem for s1:
s1 c1 e1c1
s1 c1 e2c1
s2 c3 e1c3
s2 c5 e1c5
If a client has multiple events with one staff, the client may repeat.
The code I have works great, except for this one aspect (above).
SELECT *
FROM (
SELECT eru AS RU, rudesc AS RUName, estaff AS StaffID
, (CASE
WHEN cdprogram.prog = client.cprog THEN ('yes: ' + cdprogram.prog)
WHEN cdprogram.prog <> client.cprog THEN ('no: ' + client.cprog)
END ) AS 'IsPrimProg'
, ROWNUMBER() OVER (PARTITION BY eru, estaff ORDER BY NEWID()) 'RowsPerStaff'
FROM staff INNER JOIN events ON sid = estaff
LEFT OUTER JOIN client ON client.cid = events.ecaseno
WHERE ...
GROUP BY eru, rudesc, estaff, sfname, slname, cdprogram.prog, client.cprog
, ecaseno, edate, cdprogram.progdesc, eser
) innerquery
WHERE innerquery.RowsPerStaff <= 2
ORDER BY RU, StaffID, RowsPerStaff
This code works great, it produces two rows for each staff. But, now I need to fix it so that the same client is not picked twice.
Thank you all!

SQL Between Statement using two Sub-queries that could return multiple values

Purpose: To perform alpha splits on work-lists based on roles assigned to a user. The below logic works if the user only has ONE alpha split (ex. A-CZZZ), but I need the flexibility for some users to have multiple splits (ex. A-CZZZ AND T-ZZZZ).
High Level:
SELECT Name
FROM PatientDatabase
WHERE Name Between
(SELECT UserRole.AlphaFrom
FROM UserRole
WHERE UserRole.HasRole = 1)
AND
(SELECT UserRole.AlphaThru
FROM UserRole
WHERE UserRole.HasRole = 1)
Potential Names in PatientDatabase:
ABC,PERSON
LMN,PERSON
XYZ,PERSON
Current User has two UserRoles (alpha splits):
A-CZZZ
T-ZZZZ
I want to return:
ABC,PERSON
XYZ,PERSON
I believe the subqueries may be necessary given the security-related logic below.
INFO:
-'Roledef->ReportingCategory1->Name' is the AlphaFrom
-'Roledef->ReportingCategory2->Name' is the AlphaThru
Current Logic for alpha split that works when only one (or on the first split if more than one):
AND T.PatId->PatNm BETWEEN
(SELECT E2.Roledef->ReportingCategory1->Name
FROM SECURITYPLUS.USR S2, SecurityPlus.UsrETMRole E2,
SecurityPlus.UsrETMApplication A2
WHERE A2.USR = S2.USERNAME AND
E2.USRETMAPPLICATION = A2.ID AND S2.Username = ?
AND E2.Roledef->Name ['Alpha')
AND
(SELECT E3.Roledef->ReportingCategory2->Name
FROM SECURITYPLUS.USR S3, SecurityPlus.UsrETMRole E3,
SecurityPlus.UsrETMApplication A3
WHERE A3.USR = S3.USERNAME AND
E3.USRETMAPPLICATION = A3.ID AND S3.Username = ?
AND E3.Roledef->Name ['Alpha')
Entire SQL Logic for context:
SELECT
T.Id,
T.PatId->Mrn,
T.PatId->PatNm,
T.Invoice,
T.Invoice->Fsc,
T.Stage->Name As Stage,
T.Status->Name As Status,
T.Invoice->InvBal,
T.ReviewDate,
T.HasNoteFlag
FROM
TaskManager.Task T
WHERE
T.TaskNm->Name = 'Insurance Followup'
AND T.Status IN (SELECT ID FROM Dict.ETMTaskStatus TS
WHERE StatusType NOT IN ('DELETED','DONE'))
AND T.Invoice->Fsc->EtmRole IN
(SELECT E1.Roledef
FROM SECURITYPLUS.USR S1, SecurityPlus.UsrETMRole E1,
SecurityPlus.UsrETMApplication A1
WHERE A1.USR = S1.USERNAME AND
E1.USRETMAPPLICATION = A1.ID AND S1.Username = ?)
AND T.PatId->PatNm BETWEEN
(SELECT E2.Roledef->ReportingCategory1->Name
FROM SECURITYPLUS.USR S2, SecurityPlus.UsrETMRole E2,
SecurityPlus.UsrETMApplication A2
WHERE A2.USR = S2.USERNAME AND
E2.USRETMAPPLICATION = A2.ID AND S2.Username = ?
AND E2.Roledef->Name ['Alpha')
AND
(SELECT E3.Roledef->ReportingCategory2->Name
FROM SECURITYPLUS.USR S3, SecurityPlus.UsrETMRole E3,
SecurityPlus.UsrETMApplication A3
WHERE A3.USR = S3.USERNAME AND
E3.USRETMAPPLICATION = A3.ID AND S3.Username = ?
AND E3.Roledef->Name ['Alpha')
This next logic returns the table I need, but I cannot figure out how to get a between statement to work with this:
SELECT E2.Roledef->ReportingCategory1->Name As AlphaFrom,
E2.Roledef->ReportingCategory2->Name As AlphaThru
FROM SECURITYPLUS.USR S2, SecurityPlus.UsrETMRole E2,
SecurityPlus.UsrETMApplication A2
WHERE A2.USR = S2.USERNAME AND
E2.USRETMAPPLICATION = A2.ID AND S2.Username = ?
AND E2.Roledef->Name ['Alpha'
Returns:
AlphaFrom AlphaThru
A CZZZ
T ZZZZ
I suspect I will have to abandon the sub-queries in order to accomplish this since attempts at joining them have failed, but am unsure how to proceed.
My eventual answer was to create a custom field in our Role dictionary, then create a Role for each letter of the alphabet and use an "IN" statement as opposed to a "BETWEEN".
Using the high level example:
SELECT Name
FROM PatientDatabase
WHERE LEFT(Name,1) In
(SELECT UserRole.Alpha
FROM UserRole
WHERE UserRole.HasRole = 1)

concatenate with 3 or 4 joins [duplicate]

This question already has answers here:
Closed 10 years ago.
Possible Duplicate:
combine multiple rows int one row with many to many
Using SQL Server I have the following tables/ data
CUS_VISIT
Cus_ID Cus_Vis_ID
1 01
1 02
2 01
and
CUS_EVENT
Cus_Event_ID Cus_ID Cus_Vis_ID
001 1 01
002 1 01
and
CUS_XREF
Cus_ID Cus_Event_ID Cus_Prt_ID Cus_Seq_No
1 001 1 1
1 001 2 1
and
CUS_PRT
Cus_ID Cus_Prt_ID Prt_Cd
1 1 1A
1 2 2A
I am trying to get the following
SQL RESULTS
Cus_ID Prt_Cd Cus_Vis_ID
1 1A,2A 1
what I end up with is
SQL RESULTS
Cus_ID Prt_Cd Cus_Vis_ID
1 1A 1
1 2A 1
The tables are linked by ...
CUS_VISIT.Cus_ID = CUS_EVENT.Cus_ID
CUS_VISIT.Cus_Vis_ID = CUS_EVENT.Cus_Vis_ID
CUS_VISIT.Cus_ID = CUS_XREF.Cus_ID
CUS_EVENT.Cus_Event_ID = CUS_XREF.Cus_Event_ID
CUS_XREF.Cus_Prt_ID = CUS_PRT.Cus_Prt_ID
CUS_XREF.Cus_ID = CUS_PRT.Cus_ID
I can almost get what I am after if I drop the CUS_XREF.Cus_Prt_ID = CUS_PRT.Cus_Prt_ID join but then I get all of the part codes (Prt_Cd) for the customer not just the ones for that visit.
here is what i have
select distinct CUS_EVENT.cus_id, CUS_EVENT.cus_visit_id,
(Select CUS_PRT.prt_cd + ',' AS [text()]
From CUS_PRT, CUS_XREF
where
CUS_EVENT.cus_id=XREF.cus_id
and CUS_EVENT.cus_event_id = XREF.cus_event_id
and CUS_XREF.cus_id=CUS_PRT.cus_id
and CUS_XREF.cus_prt_id = CUS_PRT.cus_prt_id
and CUS_XREF.prt_seq_no ='1'
order by CUS_PRT.prt_cd
for XML PATH('')) [Codes]
from CUS_EVENT
i made a similar post recently but didn't get any specific help. i think i need another sub-query some where. thanks for looking at this.
Well I agree that there's numerous questions about it. You just need to write for xml
select
V.Cus_ID,
V.Cus_Vis_ID,
stuff(
(
select ', ' + TP.Prt_Cd
from CUS_EVENT as TE
inner join CUS_XREF as TX on TX.Cus_Event_ID = TE.Cus_Event_ID and TX.Cus_ID = TE.Cus_ID
inner join CUS_PRT as TP on TP.Cus_Prt_ID = TX.Cus_Prt_ID and TP.Cus_ID = TE.Cus_ID
where
TE.Cus_Vis_ID = V.Cus_Vis_ID and
TE.Cus_ID = V.Cus_ID
for xml path(''), type
).value('.', 'nvarchar(max)')
, 1, 2, '')
from CUS_VISIT as V
SQL FIDDLE
Please try this... But i haven't excuted this query. I have implemented same scenerio. Just try and let me know
SELECT
CUS.Cus_ID
, CUS.Cus_Vis_ID
, Prt_Cd=STUFF(
(SELECT
', ' + S.Prt_Cd
FROM CUS_PRT s
INNER JOIN CUS_XREF XREF ON CUS.cus_id=XREF.cus_id AND s.cus_id=XREF.cus_id AND XREF.Cus_Prt_ID = s.Cus_Prt_ID
FOR XML PATH(''), TYPE
).value('.','varchar(max)')
,1,2, ''
)
FROM CUS_EVENT CUS
GROUP BY CUS.Cus_ID,CUS.Cus_Vis_ID