TSQL Recursive Query Update Temp Table - sql

I have a query that is recursively going through my employee ORG and getting a list of all people that report up to the VP. This query is working as intended:
DECLARE #pit AS DATETIME = GETDATE();
DECLARE #table TABLE (
mgrQID VARCHAR (64) ,
QID VARCHAR (64) ,
NTID VARCHAR (64) ,
FullName VARCHAR (256),
lvl INT ,
metadate DATETIME ,
totalCount INT );
WITH empList (mgrQID, QID, NTID, FullName, lvl, metadate)
AS (SELECT TOP 1 mgrQID,
QID,
NTID,
FirstName + ' ' + LastName,
0,
Meta_LogDate
FROM dbo.EmployeeTable_Historical
WHERE QID IN (SELECT director
FROM dbo.attritionDirectors)
AND Meta_LogDate <= #pit
ORDER BY Meta_LogDate DESC
UNION ALL
SELECT b.mgrQID,
b.QID,
b.NTID,
b.FirstName + ' ' + b.LastName,
lvl + 1,
b.Meta_LogDate
FROM empList AS a CROSS APPLY dbo.Fetch_DirectsHistorical_by_qid (a.QID, #pit) AS b)
-- Insert into the counts table
INSERT INTO #table (mgrQID, QID, NTID, FullName, lvl, metadate, totalCount)
SELECT empList.mgrQID,
empList.QID,
empList.NTID,
empList.FullName,
empList.lvl,
empList.metadate,
'0'
FROM empList
ORDER BY lvl
OPTION (MAXRECURSION 10);
As you can see, I have a table column called totalCount which I set to 0 in the first recursive query.
I now have a second query that goes through all of the people in that temp table and finds the total direct reports up to them.
For example if a Director Had 3 Managers and Each Manager has 3 Employees it would be 12 people reporting up to the director; the 9 employees and the 3 managers.
This comes from the query below:
;WITH a
AS (SELECT mgrQID AS direct,
QID
FROM #table AS t
WHERE QID IN (SELECT QID
FROM #table)
UNION ALL
SELECT a.direct,
t.QID
FROM #table AS t
INNER JOIN
a
ON t.mgrQID = a.QID)
--subtracting 1 because it is also counting the manager
SELECT direct,
count(*) - 1 AS totalCount
FROM a
GROUP BY direct
OPTION (MAXRECURSION 10);
My question is...
How can I update #temp totalCount with the count I get from the second query? QID and Direct are the 2 fields in common amongst the two.

Try this:
update t
set t.totalCount = a.count(*) - 1
from a
join #temp t
on a.Direct = t.QID
group by a.direct, t.QID
option (maxrecursion 10)

Related

Increment values by column into rows-- SQL Server [closed]

Closed. This question needs debugging details. It is not currently accepting answers.
Edit the question to include desired behavior, a specific problem or error, and the shortest code necessary to reproduce the problem. This will help others answer the question.
Closed 4 years ago.
Improve this question
I have the following type of data
CREATE TABLE #tmp
(
Room [NVARCHAR](50) NOT NULL,
iking INT,
iqueen INT,
isingle INT,
idouble INT
)
INSERT INTO #tmp
VALUES ('Marriot', 0, 1, 2, 1),
('Hilton', 1, 2, 0, 1)
I tried Cross Apply and case statements
I add data into temp table and wrote 4 cross apply functions for each column
King
SELECT tk.Room, tk.iking, Type = CONCAT('BED', t.n)
INTO #tempking1
FROM #tmp tk
CROSS APPLY
(SELECT TOP (tk.iking)
n = ROW_NUMBER() OVER (ORDER BY o.object_id)
FROM sys.objects o) t
ORDER BY tk.Room;
--select * from #tempking1
Queen
SELECT
tq.Room, tq.iQueen,
Type = CASE WHEN ROOM in (SELECT Distinct ROOM FROM #tempking1)
THEN CONCAT('BED', t.n + 1)
ELSE CONCAT('BED', t.n)
END
INTO #tempQueen1
FROM #tmp tq
CROSS APPLY
(SELECT TOP (tq.iQueen)
n = ROW_NUMBER() OVER (ORDER BY o.object_id)
FROM sys.objects o) t
ORDER BY tq.Room;
--select * from #tempqueen1
Single
SELECT
tq.Room, tq.isingle,
Type = CASE WHEN ROOM IN (SELECT Distinct ROOM FROM #tempking1)
THEN CONCAT('BED', t.n + 1)
WHEN ROOM IN (SELECT Distinct ROOM FROM #tempqueen1)
THEN CONCAT('BED', t.n + 1)
ElSE CONCAT('BED', t.n)
END
INTO #tempsingle1
FROM #tmp tq
CROSS APPLY
(SELECT TOP (tq.isingle)
n = ROW_NUMBER() OVER (ORDER BY o.object_id)
FROM sys.objects o) t
ORDER BY tq.Room;
--select * from #tempsingle1
Double
SELECT
tq.Room, tq.isingle,
Type = CASE WHEN ROOM IN (SELECT Distinct ROOM FROM #tempking1)
THEN CONCAT('BED', t.n + 1)
WHEN ROOM IN (SELECT Distinct ROOM FROM #tempqueen1)
THEN CONCAT('BED', t.n + 1)
WHEN ROOM IN (SELECT Distinct ROOM FROM #tempsingle1)
THEN CONCAT('BED', t.n + 1)
ELSE CONCAT('BED', t.n)
END
INTO #tempdouble1
FROM #tmp tq
CROSS APPLY
(SELECT TOP (tq.isingle)
n = ROW_NUMBER() OVER (ORDER BY o.object_id)
FROM sys.objects o) t
ORDER BY tq.Room;
--select * from #tempDouble1
SELECT Room, Type, 'King' AS Descp FROM #tempKing1
UNION ALL
SELECT Room, Type, 'Queeen' AS Descp FROM #tempQueen1
UNION ALL
SELECT Room, Type, 'Single' AS Descp FROM #tempsingle1
UNION ALL
SELECT Room, Type, 'Double' AS Descp FROM #tempDouble1
but I got
My excepted output is
Could you please help me
You can use UNPIVOT operator to fix your solution. Try this script:
drop TABLE #tmp
go
drop TABLE #Hotel
go
CREATE TABLE #tmp
(
Room nvarchar(30) NOT NULL
,iking int
,iqueen int
,isingle int
,idouble int
)
CREATE TABLE #Hotel
(
Room NVARCHAR(30)
,RoomType NVARCHAR(30)
,Total INT
)
Insert into #tmp Values ('Marriot', 0,1,2,1),('Hilton', 1,2,0,1)
INSERT INTO #Hotel
SELECT Room, RoomType, Total
FROM
(SELECT Room,iking,iqueen,isingle,idouble
FROM #tmp) p
UNPIVOT
(Total FOR RoomType IN
(iking,iqueen,isingle,idouble)
)AS unpvt
SELECT Room
,RoomType
--,'Bed'+CAST(Number AS VARCHAR) AS [Desc]
,'Bed'+CAST(ROW_NUMBER() OVER(PARTITION BY Room ORDER BY RoomType,Number) AS VARCHAR) AS [Desc]
,'Bed'+CAST(Total AS varchar) [Desc2]
FROM #Hotel
INNER JOIN master.dbo.spt_values N ON Total>=N.Number AND N.type='P' AND number<>0

Delete the same reference multiple columns rows safely?

I need to find all the parent-children relationships, which are all linked to my primary column ID
How I can delete the same reference columns in the table? Let say for example,if I want to delete "Google", I have to delete "HP" and Intel first also the child of HP as well.
I have tried the below thus far, but that works with only one column.
WITH tb (id,Name, Level, Path, Parent)
AS
(
SELECT
id,Name, 1 AS Level,
CAST('/'+Name as nvarchar(max)) as Path,
CAST(NULL as nvarchar(max)) as Parent
FROM krishtest
WHERE parent1 IS NULL
UNION All
SELECT
e.id,e.Name, x.Level + 1 AS Level, x.Path + '/' + e.Name as Path,
REVERSE(SUBSTRING( REVERSE(x.[Path]) ,0 , CHARINDEX( '/', REVERSE(x.[Path])) )) as [Parent]
FROM krishtest e
JOIN tb x ON x.id = e.parent1
)
SELECT Name, Level, Path, Parent FROM tb
is this use full?
declare #tmp table (id int, Name varchar(10),Parent1 int,Parent2 int,Parent3 int,Parent4 int,Parent5 int)
insert into #tmp
SELECT 1,'Microsoft',NULL,NULL,NULL,NULL,NULL
union
SELECT 2,'Google',1,NULL,NULL,NULL,NULL
union
SELECT 3,'HP',NULL,2,NULL,NULL,NULL
union
SELECT 4,'Amazone',NULL,NULL,3,NULL,NULL
union
SELECT 5,'FB',NULL,NULL,NULL,4,NULL
union
SELECT 6,'Yahoo',NULL,NULL,NULL,4,NULL
union
SELECT 7,'Intel',NULL,NULL,2,NULL,NULL
union
SELECT 8,'Apple',7,5,NULL,NULL,NULL
select * from #tmp
;with name_tree as (
select *
from #tmp
where id = 2
union all
select c.*
from #tmp c
join name_tree p on (p.id = c.parent1 or p.id = c.parent2 or p.id = c.parent3 or p.id = c.parent4 or p.id = c.parent5)
)
delete from t
from #tmp t
JOIN name_tree c on t.id=c.id
select * from #tmp
I haven't provided an actual solution to your issue here. But I would recommend investigating recursive Common Table Expressions. That should allow you to find all the parent records, then you can run a delete on them.
https://technet.microsoft.com/en-us/library/ms186243(v=sql.105).aspx
You can simply modify the recursive CTE's where clause like in below query to get all rows that need to be deleted.
See live demo
create table krishtest (id int, name varchar(100), parent1 int, parent2 int)
insert into krishtest values
(1,'Microsoft', NULL, NULL),
(2,'Google',1,NULL),
(3,'HP',NULL,2),
(4,'amazon',3,NULL),
(5,'FB',NULL,4),
(6,'yahoo',3,NULL),
(7,'cisco',6,NULL)
;
WITH tb (id,Name, Level, Path, Parent)
AS
(
SELECT
id,Name, 1 AS Level,
CAST('/'+Name as nvarchar(max)) as Path,
CAST(NULL as nvarchar(max)) as Parent
FROM krishtest
WHERE -- COALESCE(parent1,parent2) IS NULL
name ='HP'
UNION All
SELECT
e.id,e.Name, x.Level + 1 AS Level, x.Path + '/' + e.Name as Path,
REVERSE(SUBSTRING( REVERSE(x.[Path]) ,0 , CHARINDEX( '/', REVERSE(x.[Path])) )) as [Parent]
FROM krishtest e
JOIN tb x ON x.id = COALESCE(e.parent1,e.parent2)
)
--delete FROM krishtest where id in( select id from tb)
--select * from krishtest
SELECT Name, Level, Path, Parent FROM tb

Comma Separated SQL Server Result Set 'JOINED' with other Columns

I have a table say ProjectMaster:
Id ProjectName
1 A
2 B
3 C
another table ProjectMeter
Id ProjectId MeterNumber
1 1 #0001
2 1 #0002
3 1 #0003
4 2 #0004
5 2 #0005
6 3 #0006
I wish to have following output
ProjectName MeterNumbers
A #0001, #0002, #0003
B #0004, #0005
C #0006
I tried this and this, but unable to solve my problem.
I cannot use a table variable.
I have a already written Stored Procedure and it brings data from many joined tables. ProjectMaster also happens to be joined in one of these tables. Now am required to fetch data from ProjectMeter, such that, each row has concatenated ProjectMeter.MeterNumber corresponding to the ProjectId in that column.
right now, I get concatenated list of all meternumbers in all the rows.
I cannot use CURSOR, TABLE variable , Temp TABLE
( I hope still something can be done to my cause)
please help.....
Try this:
SELECT projectname, STUFF((SELECT distinct ', ' + meternumber
from projectmeter m
where p.id = m.projectid
FOR XML PATH(''), TYPE
).value('.', 'NVARCHAR(MAX)')
,1,1,'') MeterNumbers
from projectmaster p
See SQL Fiddle with Demo
DECLARE #ProjectMaster AS TABLE
(
ID INT IDENTITY(1, 1) ,
ProjectName VARCHAR(2)
)
DECLARE #ProjectMeter AS TABLE
(
ID INT IDENTITY(1, 1) ,
ProjectID INT ,
MeterNumber VARCHAR(50)
)
INSERT INTO #ProjectMaster
( ProjectName )
VALUES ( 'A' )
INSERT INTO #ProjectMeter
( ProjectID, MeterNumber )
VALUES ( 1, '#0001' )
INSERT INTO #ProjectMeter
( ProjectID, MeterNumber )
VALUES ( 1, '#0002' )
SELECT pMaster.ID, STUFF(( SELECT ',' + MeterNumber
FROM #ProjectMeter
FOR
XML PATH('')
), 1, 1, '') AS 'Concat Result'
FROM #ProjectMeter pMeter
INNER JOIN #ProjectMaster pMaster ON pMaster.ID = pMeter.ProjectID
GROUP BY pMaster.ID
I have used table variables here but surely you just need to drop the #'s as I have used the same table names as you have specified? Not sure if this is okay? :)
Also in MS SQL you can do it using recursive query with CTE.
Here is a SQLFiddle demo
;with t1 as (
select t.*,
cast(meternumber as varchar(max)) as m2,
0 as level
from ProjectMeter t
where not exists
(select id
from ProjectMeter l
where l.id<t.id and l.ProjectId=t.ProjectID
)
union all
select b.*,
cast(c.m2+','+b.MeterNumber as varchar(max)) as m2,
c.level+1 as level
from ProjectMeter b
inner join t1 c
on (c.id < b.id) and (b.ProjectID=c.ProjectId)
)
select pm.ProjectName as ProjectName,
t1.m2 as MeterNumbers
from t1
inner join
(select ProjectId,max(level) ml
from t1
group by ProjectId
) t2
on (t1.ProjectId=t2.ProjectID) and (t1.level=t2.ml)
left join ProjectMaster pm
on (t1.ProjectId=pm.Id)
order by t1.ProjectID

SQL query problem

Let´s say I have two tables, "Garden" and "Flowers". There is a 1:n-relationship between these tables, because in a garden can be many flowers. Is it possible to write an SQL query which returns a result with the following structure:
GardenName Flower1Name Flower2Name .... (as long as there are entries in flowers)
myGarden rose tulip
CREATE TABLE #Garden (Id INT, Name VARCHAR(20))
INSERT INTO #Garden
SELECT 1, 'myGarden' UNION ALL
SELECT 2, 'yourGarden'
CREATE TABLE #Flowers (GardenId INT, Flower VARCHAR(20))
INSERT INTO #Flowers
SELECT 1, 'rose' UNION ALL
SELECT 1, 'tulip' UNION ALL
SELECT 2, 'thistle'
DECLARE #ColList nvarchar(max)
SELECT #ColList = ISNULL(#ColList + ',','') + QUOTENAME('Flower' + CAST(ROW_NUMBER() OVER (ORDER BY (SELECT 0)) AS VARCHAR))
FROM #Flowers WHERE GardenId = (
SELECT TOP 1 GardenId
FROM #Flowers
ORDER BY COUNT(*) OVER (PARTITION BY GardenId) DESC
)
EXEC (N'
;WITH cte As
(
SELECT *, ''Flower'' + CAST(ROW_NUMBER() OVER (PARTITION BY GardenId ORDER BY (SELECT 0)) AS VARCHAR) RN
FROM #Flowers F
)
SELECT Name,' + #ColList + N'
FROM cte
JOIN #Garden g ON g.Id = GardenId
PIVOT (MAX(Flower) FOR RN IN (' + #ColList + N')) Pvt')
DROP TABLE #Garden
DROP TABLE #Flowers
Returns
Name Flower1 Flower2
-------------------- -------------------- --------------------
myGarden rose tulip
yourGarden thistle NULL
Look at using Pivot in SQL Server. Here is a good link that goes over how it works:
http://www.kodyaz.com/articles/t-sql-pivot-tables-in-sql-server-tutorial-with-examples.aspx
Ok, i think i got it working. Try this:
with temp as
(
select 'myGarden' as name, 'test1' as flower
union
select 'myGarden','test2'
union
select 'myGarden','test5'
union
select 'abeGarden','test4'
union
select 'abeGarden','test5'
union
select 'martinGarden', 'test2'
)
select* from temp
pivot
(
max(flower)
for flower in (test1,test2,test3,test4,test5)
) PivotTable
You could also make the values in the in clause dynamic. Since this is a CTE i can't in my example.
Dynamic SQL with a cursor is the only way I can think of, and it won't be pretty.
If you only want the results for one garden at a time this would give you the data:
select gardenName from tblGarden where gardenid = 1
Union ALL
select tblFLowers.flowerName from tblFlowers where gardenid = 1

Grouping runs of data

SQL Experts,
Is there an efficient way to group runs of data together using SQL?
Or is it going to be more efficient to process the data in code.
For example if I have the following data:
ID|Name
01|Harry Johns
02|Adam Taylor
03|John Smith
04|John Smith
05|Bill Manning
06|John Smith
I need to display this:
Harry Johns
Adam Taylor
John Smith (2)
Bill Manning
John Smith
#Matt: Sorry I had trouble formatting the data using an embedded html table it worked in the preview but not in the final display.
Try this:
select n.name,
(select count(*)
from myTable n1
where n1.name = n.name and n1.id >= n.id and (n1.id <=
(
select isnull(min(nn.id), (select max(id) + 1 from myTable))
from myTable nn
where nn.id > n.id and nn.name <> n.name
)
))
from myTable n
where not exists (
select 1
from myTable n3
where n3.name = n.name and n3.id < n.id and n3.id > (
select isnull(max(n4.id), (select min(id) - 1 from myTable))
from myTable n4
where n4.id < n.id and n4.name <> n.name
)
)
I think that'll do what you want. Bit of a kludge though.
Phew! After a few edits I think I have all the edge cases sorted out.
I hate cursors with a passion... but here's a dodgy cursor version...
Declare #NewName Varchar(50)
Declare #OldName Varchar(50)
Declare #CountNum int
Set #CountNum = 0
DECLARE nameCursor CURSOR FOR
SELECT Name
FROM NameTest
OPEN nameCursor
FETCH NEXT FROM nameCursor INTO #NewName
WHILE ##FETCH_STATUS = 0
BEGIN
if #OldName <> #NewName
BEGIN
Print #OldName + ' (' + Cast(#CountNum as Varchar(50)) + ')'
Set #CountNum = 0
END
SELECT #OldName = #NewName
FETCH NEXT FROM nameCursor INTO #NewName
Set #CountNum = #CountNum + 1
END
Print #OldName + ' (' + Cast(#CountNum as Varchar(50)) + ')'
CLOSE nameCursor
DEALLOCATE nameCursor
My solution just for kicks (this was a fun exercise), no cursors, no iterations, but i do have a helper field
-- Setup test table
DECLARE #names TABLE (
id INT IDENTITY(1,1),
name NVARCHAR(25) NOT NULL,
grp UNIQUEIDENTIFIER NULL
)
INSERT #names (name)
SELECT 'Harry Johns' UNION ALL
SELECT 'Adam Taylor' UNION ALL
SELECT 'John Smith' UNION ALL
SELECT 'John Smith' UNION ALL
SELECT 'Bill Manning' UNION ALL
SELECT 'Bill Manning' UNION ALL
SELECT 'Bill Manning' UNION ALL
SELECT 'John Smith' UNION ALL
SELECT 'Bill Manning'
-- Set the first id's group to a newid()
UPDATE n
SET grp = newid()
FROM #names n
WHERE n.id = (SELECT MIN(id) FROM #names)
-- Set the group to a newid() if the name does not equal the previous
UPDATE n
SET grp = newid()
FROM #names n
INNER JOIN #names b
ON (n.ID - 1) = b.ID
AND ISNULL(b.Name, '') <> n.Name
-- Set groups that are null to the previous group
-- Keep on doing this until all groups have been set
WHILE (EXISTS(SELECT 1 FROM #names WHERE grp IS NULL))
BEGIN
UPDATE n
SET grp = b.grp
FROM #names n
INNER JOIN #names b
ON (n.ID - 1) = b.ID
AND n.grp IS NULL
END
-- Final output
SELECT MIN(id) AS id_start,
MAX(id) AS id_end,
name,
count(1) AS consecutive
FROM #names
GROUP BY grp,
name
ORDER BY id_start
/*
Results:
id_start id_end name consecutive
1 1 Harry Johns 1
2 2 Adam Taylor 1
3 4 John Smith 2
5 7 Bill Manning 3
8 8 John Smith 1
9 9 Bill Manning 1
*/
Well, this:
select Name, count(Id)
from MyTable
group by Name
will give you this:
Harry Johns, 1
Adam Taylor, 1
John Smith, 2
Bill Manning, 1
and this (MS SQL syntax):
select Name +
case when ( count(Id) > 1 )
then ' ('+cast(count(Id) as varchar)+')'
else ''
end
from MyTable
group by Name
will give you this:
Harry Johns
Adam Taylor
John Smith (2)
Bill Manning
Did you actually want that other John Smith on the end of your results?
EDIT: Oh I see, you want consecutive runs grouped. In that case, I'd say you need a cursor or to do it in your program code.
How about this:
declare #tmp table (Id int, Nm varchar(50));
insert #tmp select 1, 'Harry Johns';
insert #tmp select 2, 'Adam Taylor';
insert #tmp select 3, 'John Smith';
insert #tmp select 4, 'John Smith';
insert #tmp select 5, 'Bill Manning';
insert #tmp select 6, 'John Smith';
select * from #tmp order by Id;
select Nm, count(1) from
(
select Id, Nm,
case when exists (
select 1 from #tmp t2
where t2.Nm=t1.Nm
and (t2.Id = t1.Id + 1 or t2.Id = t1.Id - 1))
then 1 else 0 end as Run
from #tmp t1
) truns group by Nm, Run
[Edit] That can be shortened a bit
select Nm, count(1) from (select Id, Nm, case when exists (
select 1 from #tmp t2 where t2.Nm=t1.Nm
and abs(t2.Id-t1.Id)=1) then 1 else 0 end as Run
from #tmp t1) t group by Nm, Run
For this particular case, all you need to do is group by the name and ask for the count, like this:
select Name, count(*)
from MyTable
group by Name
That'll get you the count for each name as a second column.
You can get it all as one column by concatenating like this:
select Name + ' (' + cast(count(*) as varchar) + ')'
from MyTable
group by Name