concatenate with 3 or 4 joins [duplicate] - sql

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

Related

Use 'Stuff' variable in WHERE clause

So here is the query code we are using:
SELECT
CONVERT(DATE, Nominations.Nomination_Date_Created) AS Nomination_Date_Created,
Nominations.Nomination_Status,
(CASE
WHEN MIN(EPORT.dbo.FDA_Divisions.division_name) = MAX(EPORT.dbo.FDA_Divisions.division_name)
THEN MIN(EPORT.dbo.FDA_Divisions.division_name)
ELSE 'Multiple Divisions'
END) AS Employee_Division,
Nominations.Nomination_Awarded_For,
Nominations.Nomination_Awarded_Other,
Nom.First_Name + ' ' + Nom.Last_Name AS Nominator_Name,
Nominations.Nomination_Group_UUID,
Nominations.Nomination_Group_Name,
Nominations.Nomination_Group_Time_off_Sum,
Nominations.Nomination_Group_Cash_Sum,
Nominations.Nomination_Type,
Nominations.Nomination_Identifier, Nominations.Nomination_Employee_UUID,
Nominations.Nomination_Nominator_ID, Nominations.Nomination_NOAC,
STUFF((SELECT ', ' + NOMGroup.division_name
FROM vw_group_nomination_divisions NOMGroup
WHERE NOMGroup.Nomination_Group_UUID = Nominations.Nomination_Group_UUID
FOR XML PATH('')), 1, 1, '') divList
FROM
Nominations
INNER JOIN
ePort.dbo.Employees AS Employees_1 ON Employees_1.CapHR_ID = Nominations.Nomination_Employee_CapHR_ID
LEFT OUTER JOIN
ePort.dbo.FDA_Offices ON Employees_1.office_id = ePort.dbo.FDA_Offices.office_id
LEFT OUTER JOIN
ePort.dbo.FDA_Centers ON Employees_1.center_ID = ePort.dbo.FDA_Centers.Center_ID
LEFT OUTER JOIN
ePort.dbo.FDA_Divisions ON Employees_1.division_id = ePort.dbo.FDA_Divisions.division_ID
LEFT OUTER JOIN
ePort.dbo.Employees AS Nom ON Nominations.Nomination_Nominator_ID = Nom.CapHR_ID
LEFT OUTER JOIN
ePort.dbo.Employees AS NomAppRTO ON Nominations.Nomination_Approving_Officer_NED_ID = NomAppRTO.CapHR_ID
GROUP BY
CONVERT(DATE, Nominations.Nomination_Date_Created),
Nominations.Nomination_Awarded_For, Nominations.Nomination_Status,
Nominations.Nomination_Awarded_Other,
Nom.First_Name + ' ' + Nom.Last_Name,
Nominations.Nomination_Type, Nominations.Nomination_Group_UUID,
Nominations.Nomination_Group_Name,
Nominations.Nomination_Group_Time_off_Sum,
Nominations.Nomination_Group_Cash_Sum,
Nominations.Nomination_Identifier, Nominations.Nomination_Type,
Nominations.Nomination_Employee_UUID,
Nominations.Nomination_Nominator_ID, Nominations.Nomination_NOAC
HAVING
(Nominations.Nomination_Type = 'Group')
AND (YEAR(CONVERT(DATE, Nominations.Nomination_Date_Created)) IN ('2020'))
ORDER BY
Nomination_Date_Created DESC, Nominations.Nomination_Group_UUID
Output:
| Id | divList |
+--------------------------------------+-------------------+
| 3462BF9B-5056-9C58-994BFFC6A38E7368 | DLR, DTD, OHCM |
| 3B8202C2-5056-9C58-99C591AA86B3A1C9 | OHCM |
| CB5A722C-5056-9C58-9983C1F6C66C0AD7 | DTD, STMD |
And the output is how we need it, however, we need to be able to search it and we cannot get that working. So how does one reference the column 'Name' that the Stuff function creates in the WHERE clause of the query?
We need to do a search within the HAVING OR WHERE clause for a value within the 'divList' column if possible. Such as divList IN ('OHCM').
Anytime I reference 'divList', I get the error:
Invalid column name 'divList'.
This would filter the results to records 1 and 2.
I hope that better explains it.
You don't want the string. Use more basic logic instead:
having sum(case when division_name = 'ccc' then 1 else 0 end) > 0
How is your temp table associated with the view. You need to join your temp table with the nominations view.
SELECT ID,
STUFF((SELECT ', ' + NOMGroup.division_name
FROM vw_group_nomination_divisions NOMGroup
WHERE NOMGroup.Nomination_Group_UUID = nom.Nomination_Group_UUID
FOR XML PATH('')), 1, 1, '') Name
FROM temp1
JOIN vw_group_nomination_divisions nom ON temp1.ID = nom.ID
WHERE Nominations.[Name] = 'ccc'
GROUP by ID

To join four table in mssql

MaingroupTable
MubGroupCodeid MainName maincode
1 Health 098
2 Social 078
SubGroup Table
SubGroupCodeid SubName subcode
1 Nursing 211
2 Civics 224
SubandMainGroup table
subandmainid **MubGroupCodeid** **subgroupcodeid**
1 1 1
2 2 2
Student Table
studid studname **subandmainid** (foriegn key of **subandmain group** table)
1 Alex 1
2 siraj 2
then I want to join and concatinate studname-maingroupcode-subgroupcode to get output like below
Alex-098-211
siraj-078-224
This will get you started and explain the joins. You'll probably also want to do some casting for the maincode and subcode, but since it's not 100% clear they aren't already varchar values I left that out.
SELECT s.studname + '-' + m.maincode + '-' + s.subcode
FROM Student s
INNER JOIN SubandMainGroup smg on smg.subandmainid = s.subandmainid
INNER JOIN MainGroup m on m.mubgroupcodeid = smg.mubgroupcodeid
INNER JOIN SubGroup s on s.subgroupcodeid = smg.subgroupcodeid
use join and concat all the required column by using ||
select s.studname ||'-'||subG.subcode ||'-' M.maincode
from
Student s join SubandMainGroup subM on s.subandmainid=subM.subandmainid
join SubGroup subG on subG.SubGroupCodeid=subM.subgroupcodeid
join MaingroupTable M on M.MubGroupCodeid=subM.MubGroupCodeid
Use the below query to solve the problem.
select stu.studname + '-'+mgrp.maincode +'-'+sgrp.subcode from Student_ stu
join Maingroup mgrp on stu.studid=mgrp.MubGroupCodeid
join SubGroup sgrp on sgrp.SubGroupCodeid=stu.studid

Is Pivot my best option? How-To

I have the following table relationships
Location.DeptFK
Dept.PK
Section.DeptFK
Subsection.SectionFK
Question.SubsectionFK
Answer.QuestionFK, SubmissionFK
Submission.PK, LocationFK
one query returns (MainTable)
QuestionNumberVar | Section | Subsection | Question | AnsYes | AnsNo | NA
1-1.1 Math Algebra Did you do your homework? 10 1 1
1-1.2 Math Algebra Did your dog eat it? 9 3 0
2-1.1 English Greek Did you do your homework? 8 0 4
The other returns (Query2)
Answer | Location | QuestionNumberVar | Critical
1 High 1-1.1 1
2 Middle 1-1.1 1
2 High 1-1.2 0
1 Middle 1-1.2 0
0 High 2-1.1 1
1 Elem 2-1.1 1
I want the (Query2) to return (IndividualTable)
QuestionNumberVar | Critical | High | Middle | Ele
1-1.1 1 1 2 'blank'
1-1.2 0 2 1 'blank'
2-1.1 1 0 'blank' 1
I then want to merge it on the end of the previous table using QuestionNumberVar as a key of sorts. so the table will look like
QuestionNumberVar | MainTable | IndividualTable
1-1.1 Data Data
1-1.2 Data Data
2-1.1 Data Data
Where the answers are grouped by QuestionNumberVar. The MainTable is not dynamic but the other IndividualTable needs to be dynamic. Location doesn't always appear for certain Dept's as seen at the relationships.
These Queries work for gathering the required Data but I don't know how to convert them to modify my tables how I would like. I think Pivot is what should be used for Query 2 to make IndividualTable I am also not sure how to mesh IndividualTable with MainTable
MainTable Query
SELECT Section.StepNumber + '-' + Question.QuestionNumber AS QuestionNumberVar,
Question.Question,
Subsection.Name AS Subsection,
Section.Name AS Section,
SUM(CASE WHEN (Answer.Answer = 0) THEN 1 ELSE 0 END) AS NA,
SUM(CASE WHEN (Answer.Answer = 1) THEN 1 ELSE 0 END) AS AnsNo,
SUM(CASE WHEN (Answer.Answer = 2) THEN 1 ELSE 0 END) AS AnsYes,
(select count(distinct Location.Abbreviation) from Department inner join Plant on location.DepartmentFK = Department.PK WHERE(Department.Name = 'insertParameter'))
as total
FROM Department inner join
section on Department.PK = section.DepartmentFK inner JOIN
subsection on Subsection.SectionFK = Section.PK INNER JOIN
question on Question.SubsectionFK = Subsection.PK INNER JOIN
Answer on Answer.QuestionFK = question.PK inner JOIN
Submission on Submission.PK = Answer.SubmissionFK inner join
Location on Location.DepartmentFK = Department.PK AND Location.pk = Submission.PlantFK
WHERE (Department.Name = 'InsertParameter') AND (Submission.MonthTested = '1/1/2017')
GROUP BY Question.Question, QuestionNumberVar, Subsection.Name, Section.Name, Section.StepNumber
ORDER BY QuestionNumberVar;
Query 2
SELECT Answer.Answer, Location.abbreviation, Section.StepNumber + '-' + Question.QuestionNumber AS QuestionNumber, Cast(Question.CriticalProcessVariable AS VARCHAR) AS CriticalProcessVariable
FROM Department left join
Section on Department.PK = Section.DepartmentFK left JOIN
Subsection on Subsection.SectionFK = Section.PK left JOIN
Question on Question.SubsectionFK = Subsection.PK left JOIN
Answer on Answer.QuestionFK = Question.PK left JOIN
Submission on Submission.PK = SubmissionFK left join
Location on Location.DepartmentFK = Department.PK AND Location.pk = Submission.PlantFK
WHERE (Department.Name = 'insertParameter') AND (Submission.MonthTested = '01/01/2017')
ORDER BY CAST(Section.StepNumber as INT) ASC, Question.QuestionNumber;
I have attempted to Pivot Query 2 to no avail. My problem always arises over not being able to sum by Answer because it doesn't recognize it (probably from not being in Location?) I am kind of lost as this is the most complex query I have ever made and I can't wrap my head around the requirements for Pivot and how to properly apply it.
Pivot was the way I handled this eventually working my way through error after error. T/SQL threw a few wrenches in my way. I first Pivoted Query 2
select #cols = STUFF((SELECT distinct ',' + QUOTENAME(PivotQ.Abbreviation)
from ( --QUERY2
) PivotQ
FOR XML PATH(''), TYPE
).value('.', 'NVARCHAR(MAX)')
,1,1,'')
set #query = 'SELECT StepNumber + ''-'' + QuestionNum as QuestionNumber, '+#cols+' from
(
--Query2
) x
pivot
(
SUM(Answer)
for Abbreviation in (' + #cols + ')
) p
'
Then I used QuestionNumber as a Key to join to MainTable's QuestionNumber. I used a second #cols to make the columns for use in the second query
select #cols2 = STUFF((SELECT distinct ', PivotJoin.' + QUOTENAME(PivotQ.Abbreviation)
from ( --QUERY2
) PivotQ
FOR XML PATH(''), TYPE
).value('.', 'NVARCHAR(MAX)')
,1,1,'')
'+ #cols2 +' --added into Select of MainTable
-- Added to From Statement
inner join
('+ #query +') PivotJoin on PivotJoin.QuestionNumber = Section.StepNumber + ''-'' + Question.QuestionNumber
I had to make The Pivot's Question Number the main because now that its T/SQL instead of pure SQL using the Alias in Group By no longer worked. I then added #cols2 to the Group By clause. and made it Order By PivotJoin.QuestionNumber instead.

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).

How to Optimize this SQL Query?

I have 3 tables:
CRSTasks (ID,parentID)
CRSTaskReceivers (ID,tskID,receiverID)
UserNames (id,name)
...relation between CRSTasks and CRSTaskReceivers one-to-many
between UserNames and CRSTaskReceivers one-to-one
tasks
ID parent
1 null
10 1
50 1
taskReceivers
id taskID receiverID
1 1 4(john)
1 10 2(mike)
1 50 3(brand)
I need result like that:
taskid Receivers
-------------------
1 jone,mike,brand
ONLY FOR PARENT TASKS IT WILL CONCATE RECEIVERS
SQL Server 2005+:
SELECT t.id AS taskid,
STUFF((SELECT ','+ x.name
FROM (SELECT COALESCE(pu.[ArabicName], aut.Name) AS name
FROM CRSTaskReceivers tr
JOIN AD_USER_TBL aut ON aut.id = tr.receiverid
LEFT JOIN PORTAL_USERS pu ON pu.id = aut.id
WHERE tr.crstaskid = t.id
AND tr.receivertype = 1
UNION
SELECT agt.name
FROM CRSTaskReceiver tr
JOIN AD_GROUP_TBL sgt ON agt.id = tr.receiverid
WHERE tr.receivertype = 3
AND tr.crstaskid = t.id) x
FOR XML PATH('')), 1, 1, '')
FROM CRSTasks t
Don't need the function.
Besides the odd string concatenation going on it sure looks like all that could be done in one query instead of four. It's perfectly fine to have more than one criteria in a join. Something along:
FROM CRSTaskReceiver
INNER JOIN CRSTask
ON CRSTaskReceiver.CRSTaskID = CRSTask.ID
INNER JOIN CRS_BuiltinGroup
ON CRSTaskReceiver.ReceiverID = CRS_BuiltinGroup.ID AND CRSTaskReceiver.ReceiverType = 4
WHERE CRSTask.ParentTask = #TaskID
Also the below part of the function seems to do absolutely nothing. What is it meant to do?
DECLARE #tmpLength INT
SET #tmpLength = 0
SET #tmpLength = LEN(#tmp)
IF #tmpLength > 0
BEGIN
SET #tmp = SUBSTRING(#tmp, 0, #tmpLength)
END