SQL query multiple rows into single row that has JOIN - sql

I am trying to combine multiple rows into a single row that involves a JOIN. I cannot figure how to get the JOIN piece to work.
I am on SQL Server 2014.
This piece works great where I am rolling rows into a single comma separated line based on ID_REVIEW:
SELECT DISTINCT
ID_REVIEW
, STUFF((
SELECT N', ' + CAST(AREA_ID AS VARCHAR(255))
FROM AREA_ASSOC t2
WHERE t1.ID_REVIEW = t2.ID
FOR XML PATH('')
), 1, 1, '') AS AREA_ID
FROM REQUEST_WORKLOAD t1
Result:
ID_REVIEW | AREA_ID
-----------------
11438 | 2
23501 | 10, 15
44677 | 8
What I'd like to do is instead of having numbers for AREA_ID is have the name of that area show up. The goal being to have:
ID_REVIEW | AREA_NM
-----------------
11438 | State St.
23501 | Main St., Second St.
44677 | Adams Ave.
This AREA_NM information is contained in another table, called AREA. In a separate query, I can do a LEFT JOIN on table AREA_ASSOC to pull in the AREA_NM:
SELECT DISTINCT
[AREA_NM]
FROM AREA_ASSOC
LEFT JOIN AREA ON
AREA_ASSOC.AREA_ID = AREA.AREA_ID
I'm stumped as to how to get that JOIN into the STUFF function so I can get the area names. Is there a way to do this?

Do the join in the subquery:
SELECT DISTINCT r2.ID_REVIEW,
STUFF( (SELECT N', ' + a2AREA_NM
FROM AREA_ASSOC aa2 JOIN
AREA a
ON aa2.AREA_ID = A.AREA_ID
WHERE aa2.ID_REVIEW = r2.ID
FOR XML PATH('')
), 1, 2, ''
) AS AREA_ID
FROM REQUEST_WORKLOAD rw

Related

SQL Query to merge several views into one

I was trying to create a new view out of 3 or 4 other views/tables.
TableA:
title_id
homeTeam
1234
WSV
5678
SSV
7890
NULL
4321
SCC
TableB:
title_id
awayTeam
1234
SSV
5678
SFV
7890
NULL
4321
KFC
TableC:
title_id
homeTeam
1234
SSV
5678
NULL
7890
AAB
4711
BFG
I would like to generate a new view out of those three which looks like:
title_id
Teams
1234
WSV, SSV, SSV
5678
SSV, SFV, N/A
7890
N/A, N/A, AAB
4321
SCC, KFC, N/A
4711
N/A, N/A, BFG
As you can see, NULL should be renamed to N/A, as well if id doesn't exist in one of the other tables. And I would like to get DISTINCT title_id.
#DaleK sure, i tried it like this:
select tableA.title_id, ISNULL(tableA.homeTeam, 'N/A') + ',' +
ISNULL(tableB.awayTeam, 'N/A') + ',' + ISNULL(tableC.homeTeam, 'N/A')
as Teams from tableA, tableB, tableC;
This leads into an neverending query for my tables which has each more than 300k rows.
Next i tried join:
select tableA.title_id, ISNULL(tableA.homeTeam, 'N/A') + ',' +
ISNULL(tableB.awayTeam, 'N/A') + ',' + ISNULL(tableC.homeTeam, 'N/A')
as Teams from tableA FULL JOIN tableB ON tableB.title_id =
tableA.title_id FULL JOIN tableC ON tableC.title_id = tableA.tile_id
But to be honest i wasnt sure about the number of rows.
You can UNION ALL the tables together, then use string aggregation
SELECT
t.title_id,
STRING_AGG(ISNULL(t.team, 'N/A'), ', ') WITHIN GROUP (ORDER BY t.ordering) AS team
FROM (
SELECT
a.title_id,
a.homeTeam AS team,
1 AS ordering
FROM TableA a
UNION ALL
SELECT
b.title_id,
b.awayTeam,
2
FROM TableB b
UNION ALL
SELECT
c.title_id,
c.homeTeam,
3
FROM TableC c
) t
GROUP BY
t.title_id;
For SQL Server 2016 and earlier, you have to use the FOR XML method, and it's less efificent as you have to query the tables multiple times
WITH t AS (
SELECT
a.title_id,
a.homeTeam AS team,
1 AS ordering
FROM TableA a
UNION ALL
SELECT
b.title_id,
b.awayTeam,
2
FROM TableB b
UNION ALL
SELECT
c.title_id,
c.homeTeam,
3
FROM TableC c
)
SELECT
tOuter.title_id,
STUFF(tInner.team.value('text()[1]', 'nvarchar(max)'), 1, LEN(', '), '') AS team
FROM (
SELECT DISTINCT t.title_id
FROM t
) tOuter
CROSS APPLY (
SELECT ', ' + ISNULL(t.team, 'N/A')
FROM t
WHERE t.title_id = tOuter.title_id
ORDER BY t.ordering
FOR XML PATH(''), TYPE
) tInner(team);
db<>fiddle
Try this:
select
COALESCE(a.title_id,b.title_id,c.title_id),
CONCAT(
ISNULL(a.homeTeam,'N/A'),
ISNULL(b.awayTeam,'N/A'),
ISNULL(c.homeTeam,'N/A'),
) 'Teams',
from TableA a
full join TableB b on a.title_id = b.title_id
full join TableC c on c.title_id = a.title_id
where
a.homeTeam is not null
OR b.awayTeam is not null
OR c.homeTeam is not null
group by a.title_id
As for the DISTINCT title_id, it should be unique by default as I assume it is used as the key in each of the tables.

SQL Server Stuff() functions to concatenate data

I am trying to retrieve data for a list of student conditions.
SELECT DISTINCT
DF.DFKEY AS StudentID
Condition = STUFF((SELECT DISTCINT ',' + DFCOND.ENR_COND
FROM DFCOND
WHERE DFCOND.SKEY = DF.DFKEY
GROUP BY DFCOND.ENR_COND
FOR XML PATH('')), 1, 1, '')
FROM
DF
LEFT JOIN
DFCOND ON df.dfkey = dfcond.skey
WHERE
DFCOND.ENR_COND IN ('12', 'CDOC', 'CONSUPP', 'CSEM')
ORDER BY
DF.DFKEY
So in this code. Each student can be assigned with many conditions but I only want to display ones that listed in WHERE IN conditions. DFCOND is the table to stored students condition data and DF is the table to store student information.
My problem is when I run it, all students conditions will be displayed so it skips the 'where in' function. How can I fix it?
For Example,
Student ID(DF.KEY) | DFCOND.ENR_COND (Conditions)
AA12 70%,12,DOC
since '12' is in the "where in" list, I only need
Student ID | condition1
AA12 12
These two tables are connected with DF.key and DFCOND.SKEY, they represent Student ID.
Thank you for any advice.
You want the filtering in the subquery:
select DF.DFKEY as StudentID,
STUFF((Select Distinct ',' + DFCOND.ENR_COND
from DFCOND
where DFCOND.SKEY = DF.DFKEY AND
DFCOND.ENR_COND in ('12', 'CDOC', 'CONSUPP', 'CSEM')
for xml path ('')
), 1, 1, ''
) as conditions
from DF
order by DF.DFKEY;
You also don't need the JOIN in the outer query. Nor the GROUP BY in the subquery.

SELECT records as comma separated string [duplicate]

This question already has answers here:
Simulating group_concat MySQL function in Microsoft SQL Server 2005?
(12 answers)
Closed 8 years ago.
So I have the following tables:
Table 1
fname mi lname empid
John A Smith 1202
Bob Doe 9815
Table 2
unid empid
1015 1202
1015 9815
Table 3
unid Item
1015 ABC
1015 DEF
My intended output should be (when supplying unid=1015)
fname mi lname item
John A Smith ABC, DEF
Bob Doe
Now that would be the ideal, but I'm more than happy to deal with the repeated [Item] values on the front end.
My current statement is:
select p.FNAME,p.MI,p.LNAME, ac.EQUIP from table1 t1, table2 t2, table3 t3
where t1.EMPID = t2.EMPID and t2.UNID = t3.UNID and t2.unid = '1015' group by t1.FNAME, t1.MI,
t1.LNAME,t3.EQUIP
For the life of me, I cannot figure out how get the values in item (which can be 0 or more to a maximum of 8) as one comma separated string. My problem is, due to site/client constraints, I cannot use an SP but this has to be done in one SQL statement.
This is on SQL SERVER 2008 R2.
Select distinct t.fname,t.mi,t.lname,
STUFF((Select distinct i.item + ','
from Table3 i
where t.unid = tt.unid AND i.unid = '1015'
ORDER BY i.unid
FOR XML PATH(''),TYPE).value('.', 'NVARCHAR(MAX)')
, 1, 0, ' ') from Table1 t
INNER JOIN Table2 tt
ON tt.empid = t.empid
group by
t.FNAME,
t.MI,
t.LNAME

trying to concatenate a column into a comma delimited list

i have 3 tables, 1 for products and one for categories the products are assigned to. what IM trying to do is concatenate the column called stCategoryName to a single column in a comma delimited list.
Basically I have the products table containing the primary key for each product and im trying to figure out how to concatenate all the stcategoryName column next to each product so i can have a simplified return
what im trying to get is the following.
stProductID stCategoryName
123 category1,category2,category3
SELECT
dbo.StoreItemTracking.StCategoryID,
dbo.StoreItemTracking.StProductID,
dbo.StoreItemTracking.viewOrder,
dbo.StoreCategories.StCategoryName,
dbo.Store_Products.PartNumber
FROM
dbo.StoreItemTracking
INNER JOIN dbo.StoreCategories
ON dbo.StoreItemTracking.StCategoryID = dbo.StoreCategories.StCategoryID
INNER JOIN dbo.Store_Products
ON dbo.StoreItemTracking.StProductID = dbo.Store_Products.ID
Im stuck as to how to concatenate a column where the query contains 3 tables to select from.
any help greatly appreciated
Look at using coalesce to turn category into a CSV:
See example:
DECLARE #EmployeeList varchar(100)
SELECT #EmployeeList = COALESCE(#EmployeeList + ', ', '')
+ CAST(Emp_UniqueID AS varchar(5))
FROM SalesCallsEmployees
WHERE SalCal_UniqueID = 1
SELECT #EmployeeList
You can also use CTE's or Subqueries. See:
http://archive.msdn.microsoft.com/SQLExamples/Wiki/View.aspx?title=createacommadelimitedlist
Another nice and easy example:
http://www.codeproject.com/Articles/21082/Concatenate-Field-Values-in-One-String-Using-CTE-i
This:
FId FName
--- ----
2 A
4 B
5 C
6 D
8 E
with:
;WITH ABC (FId, FName) AS
(
SELECT 1, CAST('' AS VARCHAR(8000))
UNION ALL
SELECT B.FId + 1, B.FName + A.FName + ', '
FROM (And the above query will return
SELECT Row_Number() OVER (ORDER BY FId) AS RN, FName FROM tblTest) A
INNER JOIN ABC B ON A.RN = B.FId
)
SELECT TOP 1 FName FROM ABC ORDER BY FId DESC
becomes:
FName
----------------------------
A, B, C, D, E,
Don't understand how your products and categories are connected but in general I do like this to create comma separated lists.
SELECT table1.Id
,Csv
FROM table1
CROSS APPLY (
-- Double select so we can have an alias for the csv column
SELECT (SELECT ',' + table2.Name
FROM table2
WHERE table2.Id = table1.Id
FOR XML PATH('')
) AS RawCsv
) AS CA1
CROSS APPLY (
-- Trim the first comma
SELECT RIGHT(RawCsv, LEN(RawCsv) - 1) AS Csv
) AS CA2

SQL: Nested SELECT with multiple values in a single field

In my SQL 2005 DB, I have a table with values stored as IDs with relationships to other tables. So in my MyDBO.warranty table, I'm storing product_id instead of product_name in order to save space. The product_name is stored in MyDBO.products.
When the marketing department pulls the demographic information, the query selects the corresponding name for each ID from related tables (trimmed down for brevity):
SELECT w1.warranty_id AS "No.",
w1.created AS "Register Date"
w1.full_name AS "Name",
w1.purchase_date AS "Purchased",
(
SELECT p1.product_name
FROM WarrDBO.products p1 WITH(NOLOCK)
WHERE p1.product_id = i1.product_id
) AS "Product Purchased",
i1.accessories
FROM WarrDBO.warranty w1
LEFT OUTER JOIN WarrDBO.warranty_info i1
ON i1.warranty_id = w1.warranty_id
ORDER BY w1.warranty_id ASC
Now, my problem is that the "accessories" column on the warranty_info table stores several values:
No. Register Date Name Purchased Accessories
---------------------------------------------------------------------
1500 1/1/2008 Smith, John Some Product 5,7,9
1501 1/1/2008 Hancock, John Another 2,3
1502 1/1/2008 Brown, James And Another 2,9
I need to do something similar with "Accessories" that I did with "Product" and pull accessory_name from the MyDBO.accessories table using accessory_id. I'm not sure where to start, because first I'd need to extract the IDs and then somehow concatenate multiple values into a string. So each line would have "accessoryname1,accessoryname2,accessoryname3":
No. Register Date Name Purchased Accessories
---------------------------------------------------------------------
1500 1/1/2008 Smith, John Some Product Case,Bag,Padding
1501 1/1/2008 Hancock, John Another Wrap,Label
1502 1/1/2008 Brown, James And Another Wrap,Padding
How do I do this?
EDIT>> Posting my final code:
I created this function:
CREATE FUNCTION SQL_GTOInc.Split
(
#delimited varchar(50),
#delimiter varchar(1)
) RETURNS #t TABLE
(
-- Id column can be commented out, not required for sql splitting string
id INT identity(1,1), -- I use this column for numbering splitted parts
val INT
)
AS
BEGIN
declare #xml xml
set #xml = N'<root><r>' + replace(#delimited,#delimiter,'</r><r>') + '</r></root>'
insert into #t(val)
select
r.value('.','varchar(5)') as item
from #xml.nodes('//root/r') as records(r)
RETURN
END
And updated my code accordingly:
SELECT w1.warranty_id,
i1.accessories,
(
CASE
WHEN i1.accessories <> '' AND i1.accessories <> 'NULL' AND LEN(i1.accessories) > 0 THEN
STUFF(
(
SELECT ', ' + a1.accessory
FROM MyDBO.accessories a1
INNER JOIN MyDBO.Split(i1.accessories, ',') a2
ON a1.accessory_id = a2.val
FOR XML PATH('')
), 1, 1, ''
)
ELSE ''
END
) AS "Accessories"
FROM MyDBO.warranty w1
LEFT OUTER JOIN MyDBO.warranty_info i1
ON i1.warranty_id = w1.warranty_id
You could write a table valued function that simply splits comma separated string into XML and turns XML nodes to rows.
See:
http://www.kodyaz.com/articles//t-sql-convert-split-delimeted-string-as-rows-using-xml.aspx
Join to accessories through the result of function call, and stuff the result back to comma separated list of names.
Untested code:
SELECT w1.warranty_id AS "No.",
w1.created AS "Register Date"
w1.full_name AS "Name",
w1.purchase_date AS "Purchased",
(
SELECT p1.product_name
FROM WarrDBO.products p1 WITH(NOLOCK)
WHERE p1.product_id = i1.product_id
) AS "Product Purchased",
STUFF(
(
SELECT
', ' + a.name
FROM [table-valued-function](i1.accessories) acc_list
INNER JOIN accessories a ON acc_list.id = a.id
FOR XML PATH('')
), 1, 1, ''
) AS [accessories]
FROM WarrDBO.warranty w1
LEFT OUTER JOIN WarrDBO.warranty_info i1
ON i1.warranty_id = w1.warranty_id
ORDER BY w1.warranty_id ASC
Nothing to do with your question. Just a note that your original query can also be written, moving the subqery to a join, as:
SELECT w1.warranty_id AS "No.",
w1.created AS "Register Date"
w1.full_name AS "Name",
w1.purchase_date AS "Purchased",
p1.product_name AS "Product Purchased",
i1.accessories
FROM WarrDBO.warranty w1
INNER JOIN WarrDBO.products p1
ON p1.product_id = i1.product_id
LEFT OUTER JOIN WarrDBO.warranty_info i1
ON i1.warranty_id = w1.warranty_id
ORDER BY w1.warranty_id ASC
You just need to use the FOR XML feature of SQL Server to easily cat strings:
Example from the linked blog post:
SELECT
STUFF(
(
SELECT
' ' + Description
FROM dbo.Brands
FOR XML PATH('')
), 1, 1, ''
) As concatenated_string
To parse a field that has already been stored as comma delimited you will have to write a UDF that parses the field and returns a table which can then be used with an IN predicate in your WHERE clause. Look here for starters, and here.
It seem to be a work for a concatenate aggregate function.
In SQL it can be deployed using CLR
http://www.mssqltips.com/tip.asp?tip=2022