Tables:
Table 1(unique values):
id | NameDep
15 X
16 Y
Table 2:
id id_department id_resource
1 15 45
2 15 47
3 16 99
....
Table 3(about 400records):
id_resource resourceName
45 name1
47 name2
99 name3
Table 4(contains multiple columns, but we only need for joining this one):
id_shift
185281
185282
185283
185284
Table 5(shiftName = table3.resourceName):
id_shift shiftName id_department
185281 name1 15
185282 name1 15
185283 name2 15
185284 name2 15
My current output is:
NameDep id resourceName shifts
X 15 name1 185281,185282,185283,185284
X 15 name2 185281,185282,185283,185284
X 15 name3 185281,185282,185283,185284
X 15 name4 185281,185282,185283,185284
X 15 name5 185281,185282,185283,185284
...etc
So basically, in resourceName i have all the data from table3
My goal is to group the shifts with the resourceName.
In my exemple 182581 and 182582 belong to resourceName = name1.
182583 and 182584 to resourceName = name2
So, I would like my output to be like this.
Desired output:
NameDep id resourceName shifts
X 15 name1 185281,185282
X 15 name2 185283,185284
...so on
I've used a temporary table, because I've read it's the only way to have a join between tables and results in xml path as I need. I can't figure out how to change the join / or use the XML PATH in order to have my desired output.
My current SQL IS:
select NameDep as region,d.id,r.resourceName
into #deps from table1 d
join table2 rd on rd.id_department=d.id
join table3 r on r.id_resource=rd.id_resource
where NameDep = 'x';
select SUBSTRING( ( select (',' + CONVERT(VARCHAR(MAX),table5.id_shift))
from table4
join table5 on table5.id_shift =table4 .id_shift
join #deps on #deps.id_department=table5.id_department
and #deps.resourceName=table5.shiftName
FOR XML PATH( '' ) ) , 2, 100000) as trip3 into #Planned
select * from (
select *
from
#deps,#Planned
) t
drop table #Planned,#deps
Thank you
You can use STUFF without using a subquery. STUFF is usually a good place to start when you're trying to compile a delimited list. Also, you don't need table 4.
In this case, you can use ',' + shift FOR XML PATH('') to create a list of comma-delimited shifts grouped by department and resource:
,185281,185282
And then you can use STUFF to chop off the first character and replace it with an empty string, leaving you with:
185281,185282
declare #t1 table (id int, namedep varchar(1))
insert into #t1 values (15, 'X')
insert into #t1 values (16, 'Y')
declare #t2 table (id int, id_department int, id_resource int)
insert into #t2 values (1, 15, 45)
insert into #t2 values (1, 15, 47)
insert into #t2 values (1, 16, 99)
declare #t3 table (id_resource int, resourceName varchar(10))
insert into #t3 values (45, 'name1')
insert into #t3 values (47, 'name2')
insert into #t3 values (99, 'name3')
declare #t5 table (id_shift int, shiftName varchar(10), id_department int)
insert into #t5 values (185281, 'name1', 15)
insert into #t5 values (185282, 'name1', 15)
insert into #t5 values (185283, 'name2', 15)
insert into #t5 values (185284, 'name2', 15)
select t1.namedep, t1.id, t3.resourceName,
stuff(
(select ',' + cast(id_shift as varchar(10))
from #t5
where shiftName = t3.resourceName and id_department = t1.id
for xml path('')), 1, 1, '') as shifts
from #t1 t1
inner join #t2 t2 on t1.id = t2.id_department
inner join #t3 t3 on t2.id_resource = t3.id_resource
Output:
namedep id resourceName shifts
X 15 name1 185281,185282
X 15 name2 185283,185284
...etc
Related
I have a table with the following values
UserID ParentID Levels Path
1 NULL 0 A1
5 1 1 A2
9 5 2 A3
43 9 3 A4
The output should be like followed :
UserID ParentID Levels FinalPath
1 NULL 0 A1/
5 1 1 A1/A2/
9 5 2 A1/A2/A3/
43 9 3 A1/A2/A3/A4/
Thanks in advance for any guidance on this.
Solution using a recusive common table expression.
Sample data
create table users
(
userid int,
parentid int,
levels int,
path nvarchar(100)
);
insert into users (userid, parentid, levels, path) values
(1, NULL, 0, 'A1'),
(5, 1, 1, 'A2'),
(9, 5, 2, 'A3'),
(43, 9, 3, 'A4');
Solution
with cte as
(
select u.userid, u.parentid, u.levels, u.path
from users u
where u.parentid is null
union all
select u.userid, u.parentid, u.levels, convert(nvarchar(100), c.path + '/' + u.path)
from users u
join cte c
on c.userid = u.parentid
)
select c.userid, c.parentid, c.levels, c.path + '/' as FinalPath
from cte c;
Fiddle
Here's a version that both calculates the Level and appends the Path.
Data
drop table if exists dbo.test_table;
go
create table dbo.test_table(
UserID int,
ParentID int,
[Path] varchar(5));
insert dbo.test_table([UserID], [ParentID], [Path]) values
(1,null, 'A1'),
(5,1, 'A2'),
(9,5, 'A3'),
(43,9, 'A4');
Query
;with recur_cte([UserId], [ParentID], h_level, [Path]) as (
select [UserId], [ParentID], 0, cast([Path] as varchar(100))
from dbo.test_table
where [ParentID] is null
union all
select tt.[UserId], tt.[ParentID], rc.h_level+1, cast(concat(tt.[Path], '/', rc.[Path]) as varchar(100))
from dbo.test_table tt join recur_cte rc on tt.[ParentID]=rc.[UserId])
select * from recur_cte;
Results
UserId ParentID h_level Path
1 NULL 0 A1
5 1 1 A1/A2
9 5 2 A1/A2/A3
43 9 3 A1/A2/A3/A4
I have multiple rows of order data which i need to consolidate in one row per part.
An example is as follows:
OrderNum PartNum Qty
-------------------------------
1 24 2
2 25 10
3 24 5
4 24 10
This then needs to be consolidated into:
OrderNum PartNum Qty
-------------------------------
1, 3, 4 24 17
2 25 10
Does anybody have any ideas how I can do this?
I have had a look around online but cannot find a solution to this use case.
Many thanks in advance,
Try this
SELECT STUFF((SELECT ',' + CAST(OrderNum AS VARCHAR(4))
FROM mytable AS s
WHERE s.PartNum = t.PartNum
FOR XML PATH('')), 1, 1, '') AS OrderNum
PartNum, SUM(Qty)
FROM mytable AS t
GROUP BY PartNum
This can be done by grouping on PartNum, sum the quantities with SUM() and concatenating strings using FOR XML PATH('') in a correlated subquery. Using FOR XML PATH('') to concatenate string is explained in this answer on SO.
DECLARE #t TABLE(OrderNum INT, PartNum INT, Qty INT);
INSERT INTO #t(OrderNum,PartNum,Qty)
VALUES(1,24,2),(2,25,10),(3,24,5),(4,24,10);
SELECT
OrderNum=STUFF((
SELECT
','+CAST(i.OrderNum AS VARCHAR)
FROM
#t AS i
WHERE
i.PartNum=o.PartNum
FOR XML
PATH(''), TYPE
).value('.[1]','VARCHAR(MAX)'),1,1,''),
o.PartNum,
Qty=SUM(o.Qty)
FROM
#t AS o
GROUP BY
o.PartNum;
Result:
OrderNum | PartNum | Qty
------------------------
1,3,4 | 24 | 17
2 | 25 | 10
SQL Server 2016 added the STRING_AGG function.
In your case, you could write
select STRING_ACC(OrderId,','),PartNum, Sum(Qty)
from MyTable
Group by PartNum
For earlier versions you'd have to use one of the techniques described by Aaron Bertrand in Grouped Concatenation in SQL Server. The fastest is to use a SQLCLR method. Next comes the FOR XML method posted by #GiorgosBetsos
DECLARE #t TABLE(OrderNum INT, PartNum INT, Qty INT)
INSERT INTO #t VALUES(1 , 24 , 2)
INSERT INTO #t VALUES(2 , 25 , 10)
INSERT INTO #t VALUES(3 , 24 , 5)
INSERT INTO #t VALUES(4 , 24 , 10)
SELECT OrderNum =
STUFF((SELECT ', ' + CONVERT(VARCHAR(50),OrderNum)
FROM #t b
WHERE b.PartNum = a.PartNum
FOR XML PATH('')), 1, 2, ''),
PartNum,
SUM(Qty) as Qty
FROM #t a
GROUP BY PartNum
Result
There are many ways to do this.
create table tablename (Name varchar(100), Rnk int)
Insert into tablename values
('Northshore', 1),
('F3', 2),
('Borderline', 3),
('Mattoon',3),
('Vinemane',5),
('Arizona',5),
('WestShore', 5),
('Schumburg', 5),
('Wilson',5)
--Method2
Select distinct
names= REPLACE(
(
Select a.Name as [data()]
From tablename A
Where A.Rnk = b.Rnk
Order by a.Name
FOR XML PATH ('') ), ' ', ',') ,Rnk
From tablename B Order by Rnk
OR
CREATE TABLE TestTable (ID INT, Col VARCHAR(4))
GO
INSERT INTO TestTable (ID, Col)
SELECT 1, 'A'
UNION ALL
SELECT 1, 'B'
UNION ALL
SELECT 1, 'C'
UNION ALL
SELECT 2, 'A'
UNION ALL
SELECT 2, 'B'
UNION ALL
SELECT 2, 'C'
UNION ALL
SELECT 2, 'D'
UNION ALL
SELECT 2, 'E'
GO
SELECT *
FROM TestTable
GO
-- Get CSV values
SELECT t.ID, STUFF(
(SELECT ',' + s.Col
FROM TestTable s
WHERE s.ID = t.ID
FOR XML PATH('')),1,1,'') AS CSV
FROM TestTable AS t
GROUP BY t.ID
GO
OR
CREATE TABLE #mable(mid INT, token nvarchar(16))
INSERT INTO #mable VALUES (0, 'foo')
INSERT INTO #mable VALUES(0, 'goo')
INSERT INTO #mable VALUES(1, 'hoo')
INSERT INTO #mable VALUES(1, 'moo')
SELECT m1.mid,
( SELECT m2.token + ','
FROM #mable m2
WHERE m2.mid = m1.mid
ORDER BY token
FOR XML PATH('') ) AS token
FROM #mable m1
GROUP BY m1.mid ;
Also, see this.
http://blog.sqlauthority.com/2009/11/25/sql-server-comma-separated-values-csv-from-table-column/
I have two tables :
Table 1:
Id | PersonId |Variable | Value|
1 12 FirstName NULL
2 12 Address NULL
------------------------
Table2:
Id | PersonId | FirstName| LastName| Address | Phone
1 12 Tommy Stark NY 12365
I need to copy data from table 2 into table 1 and
I need output like:
Table 1:
Id | PersonId |Variable | Value|
1 12 FirstName Tommy
2 12 Address NY
You could use a series of case expressions to match table 1 values to table 2 column names. It's clunky as heck, but it should work:
UPDATE t1
SET t1.value = CASE t1.variable
WHEN 'FirstName' THEN t2.firstname
ELSE t1.value
END,
t1.value = CASE t1.variable
WHEN 'LastName' THEN t2.lastname
ELSE t1.value
END,
t1.value = CASE t1.variable
WHEN 'Address' THEN t2.address
ELSE t1.value
END,
t1.value = CASE t1.phone
WHEN 'Phone' THEN t2.phone
ELSE t1.value
END
FROM t1
JOIN t2 ON t1.personid = t2.personid
Declare #Table1 TABLE
(Id int, PersonId int, Variable varchar(9), Value varchar(4))
;
INSERT INTO #Table1
(Id, PersonId, Variable, Value)
VALUES
(1, 12, 'FirstName', NULL),
(2, 12, 'Address', NULL)
;
DECLARE #Table2 TABLE
(Id int, PersonId int, FirstName varchar(5), LastName varchar(5), Address varchar(2), Phone int)
;
INSERT INTO #Table2
(Id, PersonId, FirstName, LastName, Address, Phone)
VALUES
(1, 12, 'Tommy', 'Stark', 'NY', 12365)
select TT.Id,
TT.PersonId,
TT.Variable,
CASE
WHEN T.col = TT.Variable
THEN T.val
END value
from #Table1 TT
INNER JOIN (
select col,val from #Table2 t CROSS APPLY (values ('Id',CAST(Id AS VARCHAR)), ('PersonId',CAST(PersonId AS VARCHAR)),
('FirstName',CAST(FirstName AS VARCHAR)),
('LastName',CAST(LastName AS VARCHAR)),
('Address',CAST(Address AS VARCHAR)),
('Phone',CAST(Phone AS VARCHAR)))cs(col,val))T
ON T.col = TT.Variable
Simplified problem with minimal information is as follows:
I have 2 source tables:
Table A:
Col: 1 2 3 4 5
Data: 18 15 16 17 10
Table B:
Col: 1 2 3 4 5
Data: 81 51 61 71 99
And a third table that contains "instructions":
Table 3:
ID Source
1 A
2 A
3 B
4 A
5 B
Based on what Table 3 tells me, I need to pick values from table A and B, to form a result table:
Col: 1 2 3 4 5
Data: 18 15 61 17 99
Try this -
Schema
create table TableA (col int, data1 int);
create table TableB (col int, data1 int);
create table TableC (col int, Source1 varchar(100));
insert into TableA values (1, 18), (2, 15), (3, 16);
insert into TableB values (1, 81), (2, 51), (3, 61);
insert into TableC values (1, 'A'), (2, 'A'), (3, 'B');
Query
SELECT o.col
,CASE
WHEN o.Source1 = 'A'
THEN a.data1
ELSE b.data1
END data
FROM TableC o
LEFT JOIN TableA a ON o.col = a.col
AND o.Source1 = 'A'
LEFT JOIN TableB b ON o.col = b.col
AND o.Source1 = 'B'
Result
Col Data
---------
1 18
2 15
3 61
----------Updated---------
Okay, as per the discussion, you need to use dynamic query. You need to first construct columns from tablec and then use it dynamic query as shown below.
DECLARE #cols AS NVARCHAR(MAX)
,#query AS NVARCHAR(MAX)
SELECT #cols = STUFF((
SELECT CASE
WHEN c.Source1 = 'A'
THEN ',' + 'a.[' + cast(c.Col AS VARCHAR(4)) + ']'
ELSE ',' + 'b.[' + cast(c.Col AS VARCHAR(4)) + ']'
END
FROM TableC c
FOR XML PATH('')
,TYPE
).value('.', 'NVARCHAR(MAX)'), 1, 1, '')
SET #query = 'SELECT ' + #cols + ' FROM TableA a
FULL OUTER JOIN TableB b ON 1 = 1'
EXECUTE sp_executesql #query;
Result
1 2 3 4
--------------
18 15 61 17
There is probably a better way but this works.
SELECT
CASE WHEN (SELECT Source FROM Table3 WHERE ID = 1) = 'A' THEN a.[1] ELSE b.[1] END
, CASE WHEN (SELECT Source FROM Table3 WHERE ID = 2) = 'A' THEN a.[2] ELSE b.[2] END
, CASE WHEN (SELECT Source FROM Table3 WHERE ID = 3) = 'A' THEN a.[3] ELSE b.[3] END
, CASE WHEN (SELECT Source FROM Table3 WHERE ID = 4) = 'A' THEN a.[4] ELSE b.[4] END
, CASE WHEN (SELECT Source FROM Table3 WHERE ID = 5) = 'A' THEN a.[5] ELSE b.[5] END
FROM dbo.TableA a
JOIN TableB b ON 1=1
I have a result set like this:
ID Value
1 100
2 50
3 200
4 30
- -
- -
I want it to transform into following:
Value1 Value2
100 50
200 30
- -
- -
How to do it with T-SQL?
Use this:
select a.Value, b.Value
from
(
select row_number() over(order by ID) [rn], Value
from #t
)a
left join
(
select row_number() over(order by ID) [rn], Value
from #t
)b on b.rn = a.rn + 1
where a.rn % 2 = 1
Sample data:
declare #t table (ID int, Value int)
insert #t values (1,100), (2,50), (3,200), (4,30)
Output:
Value Value
----------- -----------
100 50
200 30
declare #t table (id int, v int)
insert #t values (1, 10)
insert #t values (2, 20)
insert #t values (3, 30)
insert #t values (4, 40)
insert #t values (5, 50)
select t1.v, t2.v
from #t t1
left join #t t2
on t1.id + 1 = t2.id
where t1.id %2 = 1