query for retrieving information from 3 tables in sql? - sql-server

i have 3 tables
1)
news_table
nid tilte
1 Hi
2 Hello
2)
Product_table
pid name
2 abc
3 def
4 rty
5 zxc
6 poj
7 lkj
3)
temp
nid pid
1 2
1 3
1 4
2 5
2 6
2 7
I want output like this
nid pids names title
1 2,3,4 abc,def,rtj hi
2 5,6,7 zxc,poj,lkj hello

This a full working example run on SQL Server 2012:
DECLARE #news_table TABLE
(
[nid] TINYINT
,[title] VARCHAR(8)
);
INSERT INTO #news_table ([nid], [title])
VALUES (1, 'Hi')
,(2, 'Hellow');
DECLARE #Product_table TABLE
(
[pid] TINYINT
,[name] VARCHAR(8)
);
INSERT INTO #Product_table ([pid], [name])
VALUES (2, 'abc')
,(3, 'def')
,(4, 'rty')
,(5, 'zxc')
,(6, 'poj')
,(7, 'lkj');
DECLARE #temp TABLE
(
[nid] TINYINT
,[pid] TINYINT
);
INSERT INTO #temp ([nid], [pid])
VALUES (1, 2)
,(1, 3)
,(1, 4)
,(2, 5)
,(2, 6)
,(2, 7);
SELECT [nid]
,[pids]
,[names]
,[title]
FROM #news_table NT
CROSS APPLY
(
SELECT STUFF
(
(
SELECT ',' + PT.[name]
FROM #temp T
INNER JOIN #Product_table PT
ON T.[pid] = PT.[pid]
WHERE T.[nid] = NT.[nid]
FOR XML PATH(''), TYPE
).value('.', 'NVARCHAR(MAX)')
,1
,1
,''
)
) DS1 ([names])
CROSS APPLY
(
SELECT STUFF
(
(
SELECT ',' + CAST(T.[pid] AS VARCHAR(12))
FROM #temp T
WHERE T.[nid] = NT.[nid]
FOR XML PATH(''), TYPE
).value('.', 'VARCHAR(MAX)')
,1
,1
,''
)
) DS2 ([pids]);
I will strongly recommend to check the following links about .net integration in SQL Server:
String Utility Functions Sample - full working examples
Stairway to SQLCLR - still in progress
Introduction to SQL Server CLR Integration - official documentation
You can defined your own aggregates. For example, for concatenating strings and the solution will be far more simple:
SELECT NT.[nid]
,[dbo].[Concatenate] (T.[pid]) AS [pids]
,[dbo].[Concatenate] ([name]) AS [names]
,NT.[title]
FROM #news_table NT
INNER JOIN #temp T
ON T.[nid] = NT.[nid]
INNER JOIN #Product_table PT
ON T.[pid] = PT.[pid]
GROUP BY NT.[nid]
,NT.[title]

Related

Can't figure out how to use XML path

Hi I have tried all I can, but can't figure out how to do this.
I have this table "T1"
----RID----PID----
1 1
1 2
which i left join on RID with another "T2" to get the name column of PID:
----RID-----PID-----Pname----
1 1 Task1
1 2 Task2
So far so good. But what I really want is at table with these columns: 1. (distinct RID) 2.(A count of PID's in each Rid) 3.( Comma-seperated string of all PID-names)
----RID-----PID-----Pname----
1 2 Task1,Task2
But if a RID only has one Pname then here should be no comma:
----RID-----PID-----Pname----
1 2 Task1,Task2
2 1 Task1
Anyone ? I have tried with XML PATH but can't figure it out.....
Full working example:
DECLARE #T1 TABLE
(
[RID] INT
,[PID] INT
);
INSERT INTO #T1 ([RID], [PID])
VALUES (1, 1)
,(1, 2);
DECLARE #T2 TABLE
(
[RID] INT
,[PID] INT
,[Pname] VARCHAR(12)
);
INSERT INTO #T2 ([RID], [PID], [Pname])
VALUES (1, 1, 'Task1')
,(1, 2, 'Task2');
SELECT T1.[RID]
,COUNT(T1.[PID]) AS [PID]
,[value] AS [PName]
FROM #T1 T1
CROSS APPLY
(
SELECT STUFF
(
(
SELECT ',' + T2.[Pname]
FROM #T2 T2
WHERE T2.[RID] = T2.[RID]
ORDER BY T2.[PID]
FOR XML PATH(''), TYPE
).value('.', 'VARCHAR(MAX)')
,1
,1
,''
)
) DS ([value])
GROUP BY T1. [RID]
,[value];

I want to convert some data to pivot in SQL Server with join and dynamic

The table, at last, is my target.
This is my demo database
create database pvtestDb;
go
use pvtestDb;
go
create table custTransaction
(
id int,
custNum int,
value nvarchar(50)
)
go
create table customers
(
id int,
custName nvarchar(50)
)
insert into Customers(id, custName)
values (1, 'aaa'), (2, 'bbb'), (3, 'ccc'), (4, 'ddd'),
(5, 'eee'), (6, 'fff'), (7, 'ggg'), (8, 'hhh'), (9, 'iii')
insert into custTransaction (id, custNum, value)
values (1, 3, 'a'), (1, 4, 'b'), (1, 5, 'c'),
(2, 3, 'd'), (2, 4, 'e'), (2, 6, 'f'),
(3, 3, 'g'), (3, 8, 'h'), (3, 9, 'i')
select * from customers
select * from custTransaction
select custName, custNum, value
from customers
join custTransaction on custTransaction.id = customers.id
I tried code like this but not worked at all
SELECT
custNum, [a], [b], [c], [d]
FROM
customers
JOIN
custTransaction ON custTransaction.id = customers.id
PIVOT
(COUNT([custName])
FOR [custName] IN ([a], [b], [c], [d])) AS p
I need to join between the two tables in first.
Any hints would be appreciated as I am stuck with this situation
Here's approach with dynamic SQL
declare #customers varchar(8000)
declare #sql varchar(8000)
select #customers = stuff((
select ',' + quotename(custName)
from customers
for xml path('')
), 1, 1, '')
set #sql = 'select
id, ' + #customers + '
from (
select
ct.id, c.custName, ct.value
from
customers c
join custTransaction ct on ct.custNum = c.id
) t
pivot (
max(value) for custName in (' + #customers + ')
) p'
exec (#sql)
Output
id aaa bbb ccc ddd eee fff ggg hhh iii
----------------------------------------------------------------
1 NULL NULL a b c NULL NULL NULL NULL
2 NULL NULL d e NULL f NULL NULL NULL
3 NULL NULL g NULL NULL NULL NULL h i

How to SELECT specific characters using SQL in SQL server database

How can I select only information that start with a #?
I have a table with 5 columns and in one of the columns i.e. the comments column there is information like:
#2345 Changed by Mark
Paul changed ticket number #5923
Someone changed ticket number #5823 and #9333
#3555 is missing from the list, can only see #5789, #9000 and #4568
In the sample of 4 rows above, I want my select statement to return only ticket numbers as shown below:
comments
#2345
#5923
#5823, #9333
#5789, #9000, #4568
Someone said regular expressions can do the work for me but I am fresh graduate and have never seen such before. Can you help me please??
Select the required fields from your database table using SQL, then perform regex operations on the result in another language such as c++, php etc. when outputting your result to the client
First Create Function To extract #numeric values
CREATE FUNCTION dbo.udf_GetNumeric
(
#strAlphaNumeric VARCHAR(256))
RETURNS VARCHAR(256
)
AS
BEGIN
DECLARE #intAlpha INT
SET #intAlpha = PATINDEX('%[^#0-9]%', #strAlphaNumeric)
BEGIN
WHILE #intAlpha > 0
BEGIN
SET #strAlphaNumeric = STUFF(#strAlphaNumeric, #intAlpha, 1, '' )
SET #intAlpha = PATINDEX('%[^#0-9]%', #strAlphaNumeric )
END
END
RETURN ISNULL(#strAlphaNumeric,0)
END
GO
By Using Below Code we can Extract the numeric values
Declare #TempTable TABLE(ID Int Identity,Value varchar(1000))
INSERT INTO #TempTable
SELECT '#2345 Changed by Mark'
UNION ALL SELECT 'Paul changed ticket number #5923'
UNION ALL SELECT 'Someone changed ticket number #5823 and #9333'
UNION ALL SELECT '#3555 is missing from the list, can only see #5789, #9000 and #4568'
SELECT ID,
RIGHT(LTRIM(REPLACE(Value,'#',' ,#')),LEN(RTRIM(LTRIM(REPLACE(Value,'#',' ,#'))))-1)AS Value FROM
(
SELECT ID,dbo.udf_GetNumeric(Value) AS Value From #TempTable
)Dt
If you need to extract Numeric values Instead of pre-fixed wirh '#' Symbol
SELECT ID,
REPLACE(RIGHT(LTRIM(REPLACE(Value,'#',' ,#')),LEN(RTRIM(LTRIM(REPLACE(Value,'#',' ,#'))))-1),'#',' ')AS Value FROM
(
SELECT ID,dbo.udf_GetNumeric(Value) AS Value From #TempTable
)Dt
OutPut
ID Value
----------
1 #2345
2 #5923
3 #5823 ,#9333
4 #3555 ,#5789 ,#9000 ,#4568
IF OBJECT_ID('tempdb..#TempTable') IS NOT NULL
Drop table #TempTable
CREATE TABLE [dbo].#TempTable(
[ID] [int] NOT NULL,
[Value] [varchar](1000) NULL
) ON [PRIMARY]
INSERT [dbo].#TempTable ([ID], [Value]) VALUES (1, N'#2345 Changed by Mark #111111,767677,33333,#5656 vbvb')
GO
INSERT [dbo].#TempTable ([ID], [Value]) VALUES (2, N'Paul changed ticket number #5923,5555555 464646 #010101,5555544rrr,wwww AND #4 ')
GO
INSERT [dbo].#TempTable ([ID], [Value]) VALUES (3, N'Someone changed ticket number #5823 and #9333,7777')
GO
INSERT [dbo].#TempTable ([ID], [Value]) VALUES (4, N'#3555 is missing from the list, can only see #5789, #9000 and #4568')
GO
;WITH cte
AS
(
SELECT ID,Split.a.value('.', 'VARCHAR(1000)') AS TablesData
FROM (
SELECT ID,CAST('<S>' + REPLACE(Value, ' ', '</S><S>') + '</S>' AS XML) AS TablesData
FROM #TempTable
) AS A
CROSS APPLY TablesData.nodes('/S') AS Split(a)
)
,Final
AS
(
SELECT ID,TablesData FROM
(
SELECT ID, Split.a.value('.', 'VARCHAR(1000)') AS TablesData,CHARINDEX('#',Split.a.value('.', 'VARCHAR(1000)')) AS Data2
FROM (
SELECT ID,CAST('<S>' + REPLACE(TablesData, ',', '</S><S>') + '</S>' AS XML) AS TablesData
FROM cte
) AS A
CROSS APPLY TablesData.nodes('/S') AS Split(a)
)DT
WHERE dt.Data2 <>0
)
SELECT DISTINCT ID
,STUFF((
SELECT ', ' + CAST(TablesData AS VARCHAR(50))
FROM Final i
WHERE i.ID = o.ID
FOR XML PATH('')
), 1, 1, '') AS ColumnsWith#ValuesOnly
FROM Final o
ORDER BY 1 ASC
OutPut
ID ColumnsWith#ValuesOnly
---------------------------
1 #2345, #111111, #5656
2 #5923, #010101, #4
3 #5823, #9333
4 #3555, #5789, #9000, #4568
The best way is to use this script below:
IF OBJECT_ID('tempdb..#TempTable') IS NOT NULL
Drop table #TempTable
CREATE TABLE [dbo].#TempTable(
[ID] [int] NOT NULL,
[Value] [varchar](1000) NULL
) ON [PRIMARY]
INSERT [dbo].#TempTable ([ID], [Value]) VALUES (1, N'#2345 Changed by Mark #111111,767677,33333,#5656 vbvb')
GO
INSERT [dbo].#TempTable ([ID], [Value]) VALUES (2, N'Paul changed ticket number #5923,5555555 464646 #010101,5555544rrr,wwww AND #4 ')
GO
INSERT [dbo].#TempTable ([ID], [Value]) VALUES (3, N'Someone changed ticket number #5823 and #9333,7777')
GO
INSERT [dbo].#TempTable ([ID], [Value]) VALUES (4, N'#3555 is missing from the list, can only see #5789, #9000 and #4568')
GO
;WITH cte
AS
(
SELECT ID,Split.a.value('.', 'VARCHAR(1000)') AS TablesData
FROM (
SELECT ID,CAST('<S>' + REPLACE(Value, ' ', '</S><S>') + '</S>' AS XML) AS TablesData
FROM #TempTable
) AS A
CROSS APPLY TablesData.nodes('/S') AS Split(a)
)
,Final
AS
(
SELECT ID,TablesData FROM
(
SELECT ID, Split.a.value('.', 'VARCHAR(1000)') AS TablesData,CHARINDEX('#',Split.a.value('.', 'VARCHAR(1000)')) AS Data2
FROM (
SELECT ID,CAST('<S>' + REPLACE(TablesData, ',', '</S><S>') + '</S>' AS XML) AS TablesData
FROM cte
) AS A
CROSS APPLY TablesData.nodes('/S') AS Split(a)
)DT
WHERE dt.Data2 <>0
)
SELECT DISTINCT ID
,STUFF((
SELECT ', ' + CAST(TablesData AS VARCHAR(50))
FROM Final i
WHERE i.ID = o.ID
FOR XML PATH('')
), 1, 1, '') AS ColumnsWith#ValuesOnly
FROM Final o
ORDER BY 1 ASC

SQL Server group by count eliminate duplicates [duplicate]

How do I get:
id Name Value
1 A 4
1 B 8
2 C 9
to
id Column
1 A:4, B:8
2 C:9
No CURSOR, WHILE loop, or User-Defined Function needed.
Just need to be creative with FOR XML and PATH.
[Note: This solution only works on SQL 2005 and later. Original question didn't specify the version in use.]
CREATE TABLE #YourTable ([ID] INT, [Name] CHAR(1), [Value] INT)
INSERT INTO #YourTable ([ID],[Name],[Value]) VALUES (1,'A',4)
INSERT INTO #YourTable ([ID],[Name],[Value]) VALUES (1,'B',8)
INSERT INTO #YourTable ([ID],[Name],[Value]) VALUES (2,'C',9)
SELECT
[ID],
STUFF((
SELECT ', ' + [Name] + ':' + CAST([Value] AS VARCHAR(MAX))
FROM #YourTable
WHERE (ID = Results.ID)
FOR XML PATH(''),TYPE).value('(./text())[1]','VARCHAR(MAX)')
,1,2,'') AS NameValues
FROM #YourTable Results
GROUP BY ID
DROP TABLE #YourTable
If it is SQL Server 2017 or SQL Server Vnext, SQL Azure you can use STRING_AGG as below:
SELECT id, STRING_AGG(CONCAT(name, ':', [value]), ', ')
FROM #YourTable
GROUP BY id
using XML path will not perfectly concatenate as you might expect... it will replace "&" with "&" and will also mess with <" and ">
...maybe a few other things, not sure...but you can try this
I came across a workaround for this... you need to replace:
FOR XML PATH('')
)
with:
FOR XML PATH(''),TYPE
).value('(./text())[1]','VARCHAR(MAX)')
...or NVARCHAR(MAX) if thats what youre using.
why the hell doesn't SQL have a concatenate aggregate function? this is a PITA.
I ran into a couple of problems when I tried converting Kevin Fairchild's suggestion to work with strings containing spaces and special XML characters (&, <, >) which were encoded.
The final version of my code (which doesn't answer the original question but may be useful to someone) looks like this:
CREATE TABLE #YourTable ([ID] INT, [Name] VARCHAR(MAX), [Value] INT)
INSERT INTO #YourTable ([ID],[Name],[Value]) VALUES (1,'Oranges & Lemons',4)
INSERT INTO #YourTable ([ID],[Name],[Value]) VALUES (1,'1 < 2',8)
INSERT INTO #YourTable ([ID],[Name],[Value]) VALUES (2,'C',9)
SELECT [ID],
STUFF((
SELECT ', ' + CAST([Name] AS VARCHAR(MAX))
FROM #YourTable WHERE (ID = Results.ID)
FOR XML PATH(''),TYPE
/* Use .value to uncomment XML entities e.g. > < etc*/
).value('.','VARCHAR(MAX)')
,1,2,'') as NameValues
FROM #YourTable Results
GROUP BY ID
DROP TABLE #YourTable
Rather than using a space as a delimiter and replacing all the spaces with commas, it just pre-pends a comma and space to each value then uses STUFF to remove the first two characters.
The XML encoding is taken care of automatically by using the TYPE directive.
Another option using Sql Server 2005 and above
---- test data
declare #t table (OUTPUTID int, SCHME varchar(10), DESCR varchar(10))
insert #t select 1125439 ,'CKT','Approved'
insert #t select 1125439 ,'RENO','Approved'
insert #t select 1134691 ,'CKT','Approved'
insert #t select 1134691 ,'RENO','Approved'
insert #t select 1134691 ,'pn','Approved'
---- actual query
;with cte(outputid,combined,rn)
as
(
select outputid, SCHME + ' ('+DESCR+')', rn=ROW_NUMBER() over (PARTITION by outputid order by schme, descr)
from #t
)
,cte2(outputid,finalstatus,rn)
as
(
select OUTPUTID, convert(varchar(max),combined), 1 from cte where rn=1
union all
select cte2.outputid, convert(varchar(max),cte2.finalstatus+', '+cte.combined), cte2.rn+1
from cte2
inner join cte on cte.OUTPUTID = cte2.outputid and cte.rn=cte2.rn+1
)
select outputid, MAX(finalstatus) from cte2 group by outputid
Install the SQLCLR Aggregates from http://groupconcat.codeplex.com
Then you can write code like this to get the result you asked for:
CREATE TABLE foo
(
id INT,
name CHAR(1),
Value CHAR(1)
);
INSERT INTO dbo.foo
(id, name, Value)
VALUES (1, 'A', '4'),
(1, 'B', '8'),
(2, 'C', '9');
SELECT id,
dbo.GROUP_CONCAT(name + ':' + Value) AS [Column]
FROM dbo.foo
GROUP BY id;
Eight years later... Microsoft SQL Server vNext Database Engine has finally enhanced Transact-SQL to directly support grouped string concatenation. The Community Technical Preview version 1.0 added the STRING_AGG function and CTP 1.1 added the WITHIN GROUP clause for the STRING_AGG function.
Reference: https://msdn.microsoft.com/en-us/library/mt775028.aspx
SQL Server 2005 and later allow you to create your own custom aggregate functions, including for things like concatenation- see the sample at the bottom of the linked article.
This is just an addition to Kevin Fairchild's post (very clever by the way). I would have added it as a comment, but I don't have enough points yet :)
I was using this idea for a view I was working on, however the items I was concatinating contained spaces. So I modified the code slightly to not use spaces as delimiters.
Again thanks for the cool workaround Kevin!
CREATE TABLE #YourTable ( [ID] INT, [Name] CHAR(1), [Value] INT )
INSERT INTO #YourTable ([ID], [Name], [Value]) VALUES (1, 'A', 4)
INSERT INTO #YourTable ([ID], [Name], [Value]) VALUES (1, 'B', 8)
INSERT INTO #YourTable ([ID], [Name], [Value]) VALUES (2, 'C', 9)
SELECT [ID],
REPLACE(REPLACE(REPLACE(
(SELECT [Name] + ':' + CAST([Value] AS VARCHAR(MAX)) as A
FROM #YourTable
WHERE ( ID = Results.ID )
FOR XML PATH (''))
, '</A><A>', ', ')
,'<A>','')
,'</A>','') AS NameValues
FROM #YourTable Results
GROUP BY ID
DROP TABLE #YourTable
An example would be
In Oracle you can use LISTAGG aggregate function.
Original records
name type
------------
name1 type1
name2 type2
name2 type3
Sql
SELECT name, LISTAGG(type, '; ') WITHIN GROUP(ORDER BY name)
FROM table
GROUP BY name
Result in
name type
------------
name1 type1
name2 type2; type3
This kind of question is asked here very often, and the solution is going to depend a lot on the underlying requirements:
https://stackoverflow.com/search?q=sql+pivot
and
https://stackoverflow.com/search?q=sql+concatenate
Typically, there is no SQL-only way to do this without either dynamic sql, a user-defined function, or a cursor.
Just to add to what Cade said, this is usually a front-end display thing and should therefore be handled there. I know that sometimes it's easier to write something 100% in SQL for things like file export or other "SQL only" solutions, but most of the times this concatenation should be handled in your display layer.
Don't need a cursor... a while loop is sufficient.
------------------------------
-- Setup
------------------------------
DECLARE #Source TABLE
(
id int,
Name varchar(30),
Value int
)
DECLARE #Target TABLE
(
id int,
Result varchar(max)
)
INSERT INTO #Source(id, Name, Value) SELECT 1, 'A', 4
INSERT INTO #Source(id, Name, Value) SELECT 1, 'B', 8
INSERT INTO #Source(id, Name, Value) SELECT 2, 'C', 9
------------------------------
-- Technique
------------------------------
INSERT INTO #Target (id)
SELECT id
FROM #Source
GROUP BY id
DECLARE #id int, #Result varchar(max)
SET #id = (SELECT MIN(id) FROM #Target)
WHILE #id is not null
BEGIN
SET #Result = null
SELECT #Result =
CASE
WHEN #Result is null
THEN ''
ELSE #Result + ', '
END + s.Name + ':' + convert(varchar(30),s.Value)
FROM #Source s
WHERE id = #id
UPDATE #Target
SET Result = #Result
WHERE id = #id
SET #id = (SELECT MIN(id) FROM #Target WHERE #id < id)
END
SELECT *
FROM #Target
Let's get very simple:
SELECT stuff(
(
select ', ' + x from (SELECT 'xxx' x union select 'yyyy') tb
FOR XML PATH('')
)
, 1, 2, '')
Replace this line:
select ', ' + x from (SELECT 'xxx' x union select 'yyyy') tb
With your query.
You can improve performance significant the following way if group by contains mostly one item:
SELECT
[ID],
CASE WHEN MAX( [Name]) = MIN( [Name]) THEN
MAX( [Name]) NameValues
ELSE
STUFF((
SELECT ', ' + [Name] + ':' + CAST([Value] AS VARCHAR(MAX))
FROM #YourTable
WHERE (ID = Results.ID)
FOR XML PATH(''),TYPE).value('(./text())[1]','VARCHAR(MAX)')
,1,2,'') AS NameValues
END
FROM #YourTable Results
GROUP BY ID
didn't see any cross apply answers, also no need for xml extraction. Here is a slightly different version of what Kevin Fairchild wrote. It's faster and easier to use in more complex queries:
select T.ID
,MAX(X.cl) NameValues
from #YourTable T
CROSS APPLY
(select STUFF((
SELECT ', ' + [Name] + ':' + CAST([Value] AS VARCHAR(MAX))
FROM #YourTable
WHERE (ID = T.ID)
FOR XML PATH(''))
,1,2,'') [cl]) X
GROUP BY T.ID
Using the Stuff and for xml path operator to concatenate rows to string :Group By two columns -->
CREATE TABLE #YourTable ([ID] INT, [Name] CHAR(1), [Value] INT)
INSERT INTO #YourTable ([ID],[Name],[Value]) VALUES (1,'A',4)
INSERT INTO #YourTable ([ID],[Name],[Value]) VALUES (1,'B',8)
INSERT INTO #YourTable ([ID],[Name],[Value]) VALUES (1,'B',5)
INSERT INTO #YourTable ([ID],[Name],[Value]) VALUES (2,'C',9)
-- retrieve each unique id and name columns and concatonate the values into one column
SELECT
[ID],
STUFF((
SELECT ', ' + [Name] + ':' + CAST([Value] AS VARCHAR(MAX)) -- CONCATONATES EACH APPLICATION : VALUE SET
FROM #YourTable
WHERE (ID = Results.ID and Name = results.[name] )
FOR XML PATH(''),TYPE).value('(./text())[1]','VARCHAR(MAX)')
,1,2,'') AS NameValues
FROM #YourTable Results
GROUP BY ID
SELECT
[ID],[Name] , --these are acting as the group by clause
STUFF((
SELECT ', '+ CAST([Value] AS VARCHAR(MAX)) -- CONCATONATES THE VALUES FOR EACH ID NAME COMBINATION
FROM #YourTable
WHERE (ID = Results.ID and Name = results.[name] )
FOR XML PATH(''),TYPE).value('(./text())[1]','VARCHAR(MAX)')
,1,2,'') AS NameValues
FROM #YourTable Results
GROUP BY ID, name
DROP TABLE #YourTable
Using Replace Function and FOR JSON PATH
SELECT T3.DEPT, REPLACE(REPLACE(T3.ENAME,'{"ENAME":"',''),'"}','') AS ENAME_LIST
FROM (
SELECT DEPT, (SELECT ENAME AS [ENAME]
FROM EMPLOYEE T2
WHERE T2.DEPT=T1.DEPT
FOR JSON PATH,WITHOUT_ARRAY_WRAPPER) ENAME
FROM EMPLOYEE T1
GROUP BY DEPT) T3
For sample data and more ways click here
If you have clr enabled you could use the Group_Concat library from GitHub
Another example without the garbage: ",TYPE).value('(./text())[1]','VARCHAR(MAX)')"
WITH t AS (
SELECT 1 n, 1 g, 1 v
UNION ALL
SELECT 2 n, 1 g, 2 v
UNION ALL
SELECT 3 n, 2 g, 3 v
)
SELECT g
, STUFF (
(
SELECT ', ' + CAST(v AS VARCHAR(MAX))
FROM t sub_t
WHERE sub_t.g = main_t.g
FOR XML PATH('')
)
, 1, 2, ''
) cg
FROM t main_t
GROUP BY g
Input-output is
************************* -> *********************
* n * g * v * * g * cg *
* - * - * - * * - * - *
* 1 * 1 * 1 * * 1 * 1, 2 *
* 2 * 1 * 2 * * 2 * 3 *
* 3 * 2 * 3 * *********************
*************************
I used this approach which may be easier to grasp. Get a root element, then concat to choices any item with the same ID but not the 'official' name
Declare #IdxList as Table(id int, choices varchar(max),AisName varchar(255))
Insert into #IdxLIst(id,choices,AisName)
Select IdxId,''''+Max(Title)+'''',Max(Title) From [dbo].[dta_Alias]
where IdxId is not null group by IdxId
Update #IdxLIst
set choices=choices +','''+Title+''''
From #IdxLIst JOIN [dta_Alias] ON id=IdxId And Title <> AisName
where IdxId is not null
Select * from #IdxList where choices like '%,%'
For all my healthcare folks out there:
SELECT
s.NOTE_ID
,STUFF ((
SELECT
[note_text] + ' '
FROM
HNO_NOTE_TEXT s1
WHERE
(s1.NOTE_ID = s.NOTE_ID)
ORDER BY [line] ASC
FOR XML PATH(''),TYPE).value('(./text())[1]','VARCHAR(MAX)')
,
1,
2,
'') AS NOTE_TEXT_CONCATINATED
FROM
HNO_NOTE_TEXT s
GROUP BY NOTE_ID

T-SQL SELECT query to return combined result of multiple tables

I am wondering if it is possible and more efficient to do something that I am presently doing in code, to do in T-SQL instead.
I have a database with courses. Each course can have different offerings which are variations of the course at different locations and at different awards.
Here's my (simplified) database structure and some sample data:
CREATE TABLE tblCourse (CourseId int, CourseName varchar(50))
CREATE TABLE tblOffering (OfferingId int, CourseId int, LocationId int, AwardId int)
CREATE TABLE tblLocation (LocationId int, LocationName varchar(50))
CREATE TABLE tblAward (AwardId int, AwardName varchar(50))
INSERT INTO tblCourse VALUES (1, 'Course A')
INSERT INTO tblCourse VALUES (2, 'Course B')
INSERT INTO tblOffering VALUES (1, 1, 1, 1)
INSERT INTO tblOffering VALUES (2, 1, 2, 1)
INSERT INTO tblOffering VALUES (3, 1, 3, 1)
INSERT INTO tblOffering VALUES (4, 1, 1, 2)
INSERT INTO tblOffering VALUES (5, 2, 3, 1)
INSERT INTO tblLocation VALUES (1, 'Location A')
INSERT INTO tblLocation VALUES (2, 'Location B')
INSERT INTO tblLocation VALUES (3, 'Location C')
INSERT INTO tblAward VALUES (1, 'Award A')
INSERT INTO tblAward VALUES (2, 'Award B')
What I want to retrieve from SQL is a single row for each course/award combination. Each row would have columns for each location and whether a course of that CourseId/AwardId combination was available. There would be now rows for course/award combinations that have no offerings.
The required result, from the sample data, would be a recordset like this:
CourseId | CourseName | AwardId | AwardName | LocationA | LocationB | LocationC
---------+------------+---------+-----------+-----------+-----------+----------
1 | Course A | 1 | Award A | True | True | True
1 | Course A | 2 | Award B | True | NULL | NULL
2 | Course B | 1 | Award A | NULL | NULL | True
(NULL could also be False)
At present I am doing a simple SELECT statement with various JOINS which gives me multiple rows for each course/award combination, then I loop through all rows in my code and build the required result. However, I don't think this is so efficient as I also need to page results.
I think I could do this fairly easily in a stored procedure by creating a temporary table and a bunch of separate queries, but I don't think that would be too efficient. Wondering if there is a better way of doing it in T-SQL???
So to clarify, what I am looking for is a T-SQL query or stored procedure that will produce the above sample recordset, and which I could adapt paging to.
NB. I am using SQL Server 2008
For Dynamic columns:
DECLARE #COLUMNS VARCHAR(max)
,#query varchar(1024)
,#True varchar(6)
SELECT #COLUMNS =
COALESCE(
#Columns + ',[' + L.LocationName + ']',
'[' + L.LocationName +']'
)
FROM tblLocation L
SELECT #True = '''True'''
SELECT #QUERY = 'SELECT C.CourseName
,A.AwardName
, pvt.*
FROM (SELECT O.OfferingID AS OID
,O.AwardID AS AID
,O.CourseID AS CID
,L.LocationName AS LID
FROM tblOffering O Inner Join tblLocation L on L.LocationID = O.LocationID) AS S
PIVOT
(
count(oID) For LID IN (' +#COLUMNS+ ')
) As pvt
inner join tblCourse C on C.CourseID = CID
inner join tblAward A on A.AwardID = pvt.AID'
EXEC (#QUERY)
GO
This will produce a paginated version of your example results:
declare #tblCourse as table (CourseId int, CourseName varchar(50))
declare #tblOffering as table (OfferingId int, CourseId int, LocationId int, AwardId int)
declare #tblLocation as table (LocationId int, LocationName varchar(50))
declare #tblAward as table (AwardId int, AwardName varchar(50))
INSERT INTO #tblCourse VALUES (1, 'Course A')
INSERT INTO #tblCourse VALUES (2, 'Course B')
INSERT INTO #tblOffering VALUES (1, 1, 1, 1)
INSERT INTO #tblOffering VALUES (2, 1, 2, 1)
INSERT INTO #tblOffering VALUES (3, 1, 3, 1)
INSERT INTO #tblOffering VALUES (4, 1, 1, 2)
INSERT INTO #tblOffering VALUES (5, 2, 3, 1)
INSERT INTO #tblLocation VALUES (1, 'Location A')
INSERT INTO #tblLocation VALUES (2, 'Location B')
INSERT INTO #tblLocation VALUES (3, 'Location C')
INSERT INTO #tblAward VALUES (1, 'Award A')
INSERT INTO #tblAward VALUES (2, 'Award B') -- This had id 1 in your example.
-- Set the following parameters to control paging:
declare #PageSize as Int = 5
declare #PageNumber as Int = 1
; with CourseAwardSummary as (
select distinct C.CourseId, C.CourseName, A.AwardId, A.AwardName,
case when exists ( select 42 from #tblOffering where CourseId = C.CourseId and AwardId = A.AwardId and LocationId = 1 ) then 'True' end as LocationA,
case when exists ( select 42 from #tblOffering where CourseId = C.CourseId and AwardId = A.AwardId and LocationId = 2 ) then 'True' end as LocationB,
case when exists ( select 42 from #tblOffering where CourseId = C.CourseId and AwardId = A.AwardId and LocationId = 3 ) then 'True' end as LocationC
from #tblCourse as C inner join
#tblOffering as O on O.CourseId = C.CourseId inner join
#tblAward as A on A.AwardId = O.AwardId
),
CourseAwardSummaryRows as (
select *, Row_Number() over ( order by CourseName, AwardName ) as RowNumber
from CourseAwardSummary
)
select CourseId, CourseName, AwardId, AwardName, LocationA, LocationB, LocationC
from CourseAwardSummaryRows
where ( #PageNumber - 1 ) * #PageSize + 1 <= RowNumber and RowNumber <= #PageNumber * #PageSize
order by CourseName, AwardName
The following query does this by joining and aggregating the offering table, and then joining the result to the course and award tables:
select c.CourseId, c.CourseName, oa.AwardId, oa.AwardName,
oa.LocationA, oa.LocationB, oa.LocationC
from tblCourse c left outer join
(select o.CourseId, o.AwardId, a.awardName
max(case when LocationName = 'Location A' then 'true' end) as LocationA,
max(case when LocationName = 'Location B' then 'true' end) as LocationB,
max(case when LocationName = 'Location C' then 'true' end) as LocationC
from tblOffering o join
tblLocation l
on o.LocationId = l.LocationId join
tblAward a
on a.awardID = o.AwardId
group by o.CourseId, o.AwardId, a.awardName
) oa
on oa.CourseId = c.CourseId

Resources