Display multiple values from two tables in one row in SQL Server - sql-server

I have the following two tables
TableA Table B
id bid bname btitle
---- ------------------------------
1 1 john titlejohn
2 1 william titlewilliam
3 1 george titlegeorge
2 bill titlebill
3 kyle titlekyle
3 seb titleseb
I need a query in SQL Server which displays the following output:
id name title
1 john,william,george titlejohn,titlewilliam,titlegeorgw
2 bill titlebill
3 kyle,seb titlekyle,titleseb
Please help.

select id, name = stuff(n.name, 1, 1, ''), title = stuff(t.title, 1, 1, '')
from TableA a
outer apply
(
select ',' + bname
from TableB x
where x.bid = a.id
for xml path('')
) n (name)
outer apply
(
select ',' + btitle
from TableB x
where x.bid = a.id
for xml path('')
) t (title)

Here's one solution. It only handles bname but you can extend it to handle btitle. Concatenating column values for a given key is not a natural thing in SQL so you need a trick to loop through the table extracting each row with same key. The trick is to create a memory table with an identity column (n say) which autoincrements on each insert. You can loop through then, picking n=1, then n=2, etc to build up the string.
create function tbl_join_name( #id int)
returns varchar(max)
as
begin
declare #tbl table (n int identity(1,1), name varchar(max), title varchar(max))
insert #tbl( name, title )
select bname, btitle from TableB where bid = #id
declare #n int = 1, #name varchar(max) = '', #count int = (select count(*) from #tbl)
while #n <= #count begin
set #name = #name + (case #name when '' then '' else ',' end) + (select name from #tbl where n = #n)
set #n = #n + 1
end
return #name
end
go
select id, tbl_join_name(id) as bname --, tbl_join_title(id) as btitle
from TableA
It's not very efficient, though. Tested with Sql Server 2008 R2.

Another way:
SELECT A.id,
STUFF((SELECT ','+bname
FROM TableB B
WHERE B.bid = A.id
FOR XML PATH('')),1,1,'') as name,
STUFF((SELECT ','+btitle
FROM TableB B
WHERE B.bid = A.id
FOR XML PATH('')),1,1,'') as title
FROM TableA A
Output:
id name title
1 john,william,george titlejohn,titlewilliam,titlegeorge
2 bill titlebill
3 kyle,seb titlekyle,titleseb

Related

Using UNPIVOT in constructed String Query Dynamic

I am building a query where I will need a UNPIVOT on dynamic columns. (abcd are example string name)
data1 data2 com fr random
1 2 a d sq
3 4 b a fd
UNPIVOT like so :
data1 data2 Name Website random
1 2 a com sq
1 2 d fr sq
3 4 b com fd
3 4 a fr fd
The matter here is for building my First table I use dynamic SQL (#QueryFinal) because of the column.
Here is my UNPIVOT dynamic Query
'SELECT data1, data2, Name, Website
FROM '+#QueryFinal+'
UNPIVOT (
Name FOR Website in ('+#WebsiteCol+')
) f;'
In my #QueryFinal I have a WHERE .... ORDER BY, it seems like the UNPIVOT can't handle it.
When I delete the WHERE and ORDER BY clause I get the error :
Incorrect syntax near the keyword 'UNPIVOT'.
Try the following dynamic-pivot:
--drop table if exists unpivottest
create table unpivotTest (data1 int, data2 int, com char(1), fr char(1))
insert into unpivotTest
select 1, 2, 'a' , 'd' union all
select 3, 4, 'b', 'a'
select * from unpivotTest
declare #colsunpivot as nvarchar(max),
#query as nvarchar(max)
select #colsunpivot = stuff((select ','+ quotename(c.name)
from sys.columns c
where c.object_id = object_id('dbo.unpivottest') and c.name not like '%data%'
for xml path('')), 1, 1, '')
set #query
= 'select data1, data2, name, website
from unpivottest
-- you cannot do the ordering here
unpivot
(
name
for website in ('+ #colsunpivot +')
) u
where data1 = 1 -- here you can use your where clause
order by data1' -- here you can do the ordering by any col
--print #query
exec sp_executesql #query;
Check a working demo here.
Even if it isn't the same column name as in the example here is the final Query built thanks for the help. I put the WHERE clause in the first SELECT and the ORDER BY in the UNPIVOT
DECLARE #QueryFinal VARCHAR(max) = #Query + #QueryBis + '
from #CALENDAR_FINAL temp
LEFT JOIN #BURSTS b on b.bur_id = temp.bur_id
LEFT JOIN digital_calendar_status dcs ON dcs.dcs_date = temp.[Date] AND dcs.dif_id = '+convert(varchar(2),#FormatId)+'
LEFT JOIN digital_calendar_event dce ON dce.dce_date = temp.[Date]
WHERE '+#ConditionConflict+#ConditionIcon+#ConditionDate
DECLARE #Pivot VARCHAR(2000)='
SELECT
DateStr, f.Conflict, f.dcs_id, f.Status, f.Website, f.Advertiser, f.Comment, f.EventName, f.EventIcone
FROM ('+#QueryFinal+') AS a
UNPIVOT
(
Advertiser
FOR Website IN ('+#WebsiteCol+')
) f
ORDER BY Date;
'

Multiple tables in the where clause SQL

I have 2 tables:-
Table_1
GetID UnitID
1 1,2,3
2 4,5
3 5,6
4 6
Table_2
ID UnitID UserID
1 1 1
1 2 1
1 3 1
1 4 1
1 5 2
1 6 3
I want the 'GetID' based on 'UserID'.
Let me explain you with an example.
For e.g.
I want all the GetID where UserID is 1.
The result set should be 1 and 2. 2 is included because one of the Units of 2 has UserID 1.
I want all the GetID where UserID is 2
The result set should be 2 and 3. 2 is included because one of Units of 2 has UserID 2.
I want to achieve this.
Thank you in Advance.
You can try a query like this:
See live demo
select
distinct userid,getid
from Table_1 t1
join Table_2 t2
on t1.unitId+',' like '%' +cast(t2.unitid as varchar(max))+',%'
and t2.userid=1
The query for this will be relatively ugly, because you made the mistake of storing CSV data in the UnitID column (or maybe someone else did and you are stuck with it).
SELECT DISTINCT
t1.GetID
FROM Table_1 t1
INNER JOIN Table_2 t2
ON ',' + t1.UnitID + ',' LIKE '%,' + CONVERT(varchar(10), t2.UnitID) + ',%'
WHERE
t2.UserID = 1;
Demo
To understand the join trick being used here, for the first row of Table_1 we are comparing ,1,2,3, against other single UnitID values from Table_2, e.g. %,1,%. Hopefully it is clear that my logic would match a single UnitID value in the CSV string in any position, including the first and last.
But a much better long term approach would be to separate those CSV values across separate records. Then, in addition to requiring a much simpler query, you could take advantage of things like indices.
try this:
declare #Table_1 table(GetID INT, UnitId VARCHAR(10))
declare #Table_2 table(ID INT, UnitId INT,UserId INT)
INSERT INTO #Table_1
SELECT 1,'1,2,3'
union
SELECT 2,'4,5'
union
SELECT 3,'5,6'
union
SELECT 4,'6'
INSERT INTO #Table_2
SELECT 1,1,1
union
SELECT 1,2,1
union
SELECT 1,3,1
union
SELECT 1,4,1
union
SELECT 1,5,2
union
SELECT 1,6,3
declare #UserId INT = 2
DECLARE #UnitId VARCHAR(10)
SELECT #UnitId=COALESCE(#UnitId + ',', '') + CAST(UnitId AS VARCHAR(5)) from #Table_2 WHERE UserId=#UserId
select distinct t.GetId
from #Table_1 t
CROSS APPLY [dbo].[Split](UnitId,',') AS AA
CROSS APPLY [dbo].[Split](#UnitId,',') AS BB
WHERE AA.Value=BB.Value
Split Function:
CREATE FUNCTION [dbo].Split(#input AS Varchar(4000) )
RETURNS
#Result TABLE(Value BIGINT)
AS
BEGIN
DECLARE #str VARCHAR(20)
DECLARE #ind Int
IF(#input is not null)
BEGIN
SET #ind = CharIndex(',',#input)
WHILE #ind > 0
BEGIN
SET #str = SUBSTRING(#input,1,#ind-1)
SET #input = SUBSTRING(#input,#ind+1,LEN(#input)-#ind)
INSERT INTO #Result values (#str)
SET #ind = CharIndex(',',#input)
END
SET #str = #input
INSERT INTO #Result values (#str)
END
RETURN
END

Split and inner join return string in row SQL Server 2008 [duplicate]

This question already has answers here:
Split function in SQL Server 2008
(4 answers)
Closed 5 years ago.
I have 2 tables:
Table1
ID Name
------------
1 Cpu
2 Vga
3 Ram
Table2 :
ID Names
-------------
1 1;2
2 1;2;3
3 2
I want result: Select in table 2 :
ID Names Names string
----------------------------
1 1;2 Cpu,Vga
2 1;2;3 Cpu,Vga,Ram
3 2 Vga
How to solve my problem? Thank you
This here will do the trick for you.
You need to create a function which can split out your rows so you can join them afterwards and use som XML to concat your rows back.
You need to create a function which can split your rows or unconcat
CREATE FUNCTION [dbo].[dba_parseString_udf] (
#stringToParse VARCHAR(8000)
, #delimiter CHAR(1)
)
RETURNS #parsedString TABLE (stringValue VARCHAR(128)) AS
BEGIN
/* Declare variables */
DECLARE #trimmedString VARCHAR(8000);
/* We need to trim our string input in case the user entered extra spaces */
SET #trimmedString = LTRIM(RTRIM(#stringToParse));
/* Let's create a recursive CTE to break down our string for us */
WITH parseCTE (StartPos, EndPos)
AS
(
SELECT 1 AS StartPos
, CHARINDEX(#delimiter, #trimmedString + #delimiter) AS EndPos
UNION ALL
SELECT EndPos + 1 AS StartPos
, CHARINDEX(#delimiter, #trimmedString + #delimiter , EndPos + 1) AS EndPos
FROM parseCTE
WHERE CHARINDEX(#delimiter, #trimmedString + #delimiter, EndPos + 1) <> 0
)
/* Let's take the results and stick it in a table */
INSERT INTO #parsedString
SELECT SUBSTRING(#trimmedString, StartPos, EndPos - StartPos)
FROM parseCTE
WHERE LEN(LTRIM(RTRIM(SUBSTRING(#trimmedString, StartPos, EndPos - StartPos)))) > 0
OPTION (MaxRecursion 8000);
RETURN;
END
Inner table here is your Table2 and split table is your table1
SQL Code to run
with result as (
SELECT [id]
,[name]
,i.stringValue
from [LegOgSpass].[dbo].[inner] as a
cross apply [dbo].[dba_parseString_udf](a.Name,';') i
)
,partresult as (
select a.id,a.name,b.name as RowstoConcat from result a
left join LegOgSpass.dbo.split b on a.stringValue = b.id
)
SELECT id,Name, Pets = STUFF((SELECT N', ' + RowstoConcat
FROM partresult AS p2
WHERE p2.name = p.name
ORDER BY RowstoConcat
FOR XML PATH(N'')), 1, 2, N'')
FROM partresult AS p
GROUP BY id,Name
ORDER BY id,Name
Result
;WITH cte AS (
SELECT * FROM Table1 t1
INNER JOIN (
SELECT id AS t2_id, VALUE AS nameid
FROM Table2 t2
CROSS APPLY STRING_SPLIT(Names,';')
) t3
ON t1.id=t3.nameid
)
SELECT id ,
STUFF((SELECT ','+ Name FROM cte c2 where c.id =c2.t2_id FOR XML PATH('')),1,1,'' )
AS [Name String],
STUFF ((SELECT ','+ cast(t2_id as varchar) from cte c2
WHERE c.id=c2.nameid FOR XML PATH('')),1,1,'' ) AS Names
FROM cte c
GROUP BY id
OUTPUT
id Name String Names
1 Cpu,Vga 1,2
2 Cpu,Vga,Ram 1,2,3
3 Vga 2
NOTE
As #plaidDK pointed out in comments that,
STRING_SPLIT() is not a function of SQL SERVER 2008, the above query will help if you find some Stored procedures to do the functionality of STRING_SPLIT() .
And Substitute STRING_SPLIT() with Stored procedure name

SQL Server Concatenate Query Results

I am new to sql server.
I have a query that retrieves the desired data from various tables.
The result looks like this with other columns, that contain things like name, removed for simplicity.
id xvalue
1 x
1 y
1 z
2 x
2 y
2 z
3 x
3 y
3 z
I would like to wrap the query with a select to concatenate the result set into an new result set like this.
id xvalue
1 x,y,z
2 x,y,z
3 x,y,z
I have tried to figure out how use the for xml path option and cannot seem to find the
correct syntax.
We have two tables in SQL one with columns (id,mdf,hist) another table with columns (id,hist). First table called historial, second table is resultado. Then we need to concat hist column when the mfn is the same and copy the result to the column hist in resultado table.
DECLARE #iterator INT
SET #iterator = 1
WHILE (#iterator < 100) /* Number of rows */
BEGIN
DECLARE #ListaIncidencias VARCHAR(MAX) = ''
SELECT #ListaIncidencias = #ListaIncidencias + ';' + p.hist
FROM historial p WHERE mfn = #iterator
SET #ListaIncidencias = SUBSTRING(#ListaIncidencias,2,LEN(#ListaIncidencias))
SELECT #ListaIncidencias as 'Incidencias'
INSERT INTO resultado(hist) VALUES(#ListaIncidencias) /* Insert results in new table */
SET #iterator = #iterator + 1
END
Try this
CREATE TABLE #Tmp
(
id INT ,
xValue VARCHAR(10)
)
INSERT INTO #Tmp VALUES ( 1, 'x' )
INSERT INTO #Tmp VALUES ( 1, 'y' )
INSERT INTO #Tmp VALUES ( 1, 'Z' )
INSERT INTO #Tmp VALUES ( 2, 'A' )
INSERT INTO #Tmp VALUES ( 2, 'B' )
SELECT id ,
( STUFF(( SELECT DISTINCT
',' + CAST(xValue AS VARCHAR(20))
FROM #Tmp
WHERE id = t.id
FOR
XML PATH('')
), 1, 1, '') ) AS Data
FROM #Tmp t
GROUP BY id
Something like this should do the trick for you. SQL Server should really just have an easy syntax for this. But it'll give you a comma and space separated list of the xValues.
SELECT id
,STUFF(
(SELECT DISTINCT
', ' + t.xvalue
FROM tableName t
WHERE t.id = t1.id
ORDER BY ', ' + t.xvalue
FOR XML PATH(''), TYPE
).value('.','varchar(max)')
,1,2, ''
) AS xvalue
FROM tableName t1
GROUP BY id
select id,Ids=Stuff((SELECT ',' + xvalue FROM t t1 WHERE t1.id=t.id
FOR XML PATH (''))
, 1, 1, '' )
from t
GROUP BY id
FIDDLE
There is very new functionality in Azure SQL Database and SQL Server 2016 to handle this exact scenario. Example:
select id, STRING_AGG(xvalue, ',') as xvalue
from some_table
group by id
STRING_AGG - https://msdn.microsoft.com/en-us/library/mt790580.aspx

SQL Match multiple rows

relationID sessionID_Ref userID_Ref
1 1 1
2 1 2
3 2 1
4 2 3
5 3 1
6 3 2
7 3 3
Okey! I'm building a messaging system with the possibility to send messages to a group of people. But I'm stuck with this SQL query where to find the sessionID depending on what users I send the message to.
For example: If I (userID: 1) send a message to userID 2, the SQL Query should return sessionID: 1
If I send a message to userID 2 and 3, it sould return: sessionID: 3
Can I do this with a single sql query, using MSSQL?
Possibly something like this:
select sessionID_Ref
from tablename
group by sessionID_Ref
having count(distinct userID_Ref) = 2
and min(userID_Ref) = 1
and max(userID_Ref) = 2
Here's a full example:
create table #tablename (
relationID int,
sessionID_Ref int,
userID_Ref int
)
insert into #tablename values(1,1,1)
insert into #tablename values(2,1,2)
insert into #tablename values(3,2,1)
insert into #tablename values(4,2,3)
insert into #tablename values(5,3,1)
insert into #tablename values(6,3,2)
insert into #tablename values(7,3,3)
create table #users (
users int
)
insert into #users values(1)
insert into #users values(3)
select t.sessionID_Ref from #tablename t
inner join #users u on t.userID_Ref = u.users
inner join (
select t.sessionID_Ref
from #tablename t
group by t.sessionID_Ref
having COUNT(t.userID_Ref) = (select COUNT(*) from #users)
) aux on aux.sessionID_Ref = t.sessionID_Ref
group by t.sessionID_Ref
having COUNT(t.userID_Ref) = (select COUNT(*) from #users)
drop table #tablename
drop table #users
SELECT TOP 1 sessionID_Ref
FROM table AS table_2
WHERE (userID_Ref = 28 OR
userID_Ref = 11) AND
((SELECT COUNT(*) AS Expr1
FROM table AS table_1
WHERE (sessionID_Ref = table_2.sessionID_Ref) AND (userID_Ref = 28) OR
(sessionID_Ref = table_2.sessionID_Ref) AND (userID_Ref = 11)) = 2) AND
((SELECT COUNT(*) AS Expr1
FROM table AS table_1
WHERE (sessionID_Ref = table_2.sessionID_Ref)) = 2)
This works, but there must be a faster way....

Resources