Sql Server While Loop with Changing Condition - sql

I have a User Table in my database that contains two fields
user_id
manager_id
I am trying to construct a query to list all of the manager_ids that are associated with a user_id in a hierarchical structure.
So if i give a user_id, i will get that users manager, followed by that persons manager all the way to the very top.
So far i have tried but it doesnt give what i need:
WITH cte(user_id, manager_id) as (
SELECT user_id, manager_id
FROM user
WHERE manager_id=#userid
UNION ALL
SELECT u.user_id, u.manager_id,
FROM user u
INNER JOIN cte c on e.manager_id = c.employee_id
)
INSERT INTO #tbl (manager_id)
select user_id, manager_id from cte;
If anyone can point me in the right direction that would be great.
I thought about a While loop but this may not be very efficient and im not too sure how to implement that.

OP asked for a while loop, and while (ha, pun) this may not be the best way... Ask and you shall receive. (:
Here is sample data I created (in the future, please provide this):
CREATE TABLE #temp (userID int, managerID int)
INSERT INTO #temp VALUES (1, 3)
INSERT INTO #temp VALUES (2, 3)
INSERT INTO #temp VALUES (3, 7)
INSERT INTO #temp VALUES (4, 6)
INSERT INTO #temp VALUES (5, 7)
INSERT INTO #temp VALUES (6, 9)
INSERT INTO #temp VALUES (7, 10)
INSERT INTO #temp VALUES (8, 10)
INSERT INTO #temp VALUES (9, 10)
INSERT INTO #temp VALUES (10, 12)
INSERT INTO #temp VALUES (11, 12)
INSERT INTO #temp VALUES (12, NULL)
While Loop:
CREATE TABLE #results (userID INT, managerID INT)
DECLARE #currentUser INT = 1 -- Would be your parameter!
DECLARE #maxUser INT
DECLARE #userManager INT
SELECT #maxUser = MAX(userID) FROM #temp
WHILE #currentUser <= #maxUser
BEGIN
SELECT #userManager = managerID FROM #temp WHERE userID = #currentUser
INSERT INTO #results VALUES (#currentUser, #userManager)
SET #currentUser = #userManager
END
SELECT * FROM #results
DROP TABLE #temp
DROP TABLE #results

Get rid of this column list in your CTE declaration that has nothing to do with the columns you are actually selecting in the CTE:
WITH cte(employee_id, name, reports_to_emp_no, job_number) as (
Just make it this:
WITH cte as (

I recommend recursive solution:
WITH Parent AS
(
SELECT * FROM user WHERE user_id=#userId
UNION ALL
SELECT T.* FROM user T
JOIN Parent P ON P.manager_id=T.user_id
)
SELECT * FROM Parent
To see demo, run following:
SELECT * INTO #t FROM (VALUES (1,NULL),(2,1),(3,2),(4,1)) T(user_id,manager_id);
DECLARE #userId int = 3;
WITH Parent AS
(
SELECT * FROM #t WHERE user_id=#userId
UNION ALL
SELECT T.* FROM #t T
JOIN Parent P ON P.manager_id=T.user_id
)
SELECT * FROM Parent

Related

SQL Server increment two tables in an update

First, SQL is NOT my strong point, as you'll see. I have one table that keeps track of the next item number, by some type, like so:
declare #maxs as table
(
Equip int,
NextId int
);
-- initial id values
insert into #maxs (Equip, NextId) values (400, 40);
insert into #maxs (Equip, NextId) values (500, 50);
If I create an item of type '400' then the next Id is 40, and that should be incremented to 41. In a case of a single add, that's easy enough. Our program does adds in batch, so here is my problem.
declare #t as table (Id int, Equip int, Descr varchar(20));
-- simulates the batch processing
insert into #t (Equip, Descr) values (400, 'Item 1');
insert into #t (Equip, Descr) values (400, 'Item 2');
insert into #t (Equip, Descr) values (500, 'Item 3');
-- generate the new id's in batch
UPDATE t
SET Id = (SELECT m.NextId + ROW_NUMBER() OVER (PARTITION BY t.Equip ORDER BY t.Equip))
FROM #t t
INNER JOIN #maxs m ON m.Equip = t.Equip
SELECT * FROM #t
This results in both Item 1 and Item 2 having the same Id because only 1 row is returned for 400, so ROW_NUMBER is the same for both. I need to be able to increment the NextId value in #maxs as well as update the entry in #t so that the second row that joins into the 400 value in #maxs will have the next value (almost like a x++ reference in c#). Is there a clean way to do that in SQL?
Thanks in advance.
Just go with JOIN and nested select
declare #t as table (Id int, Equip int, Descr varchar(20));
-- simulates the batch processing
insert into #t (Equip, Descr) values (400, 'Item 1');
insert into #t (Equip, Descr) values (400, 'Item 2');
insert into #t (Equip, Descr) values (500, 'Item 3');
-- generate the new id's in batch
UPDATE t
SET
Id = t.Equip + s.RowNum
FROM #t t
JOIN (select Equip,
Descr,
ROW_NUMBER() OVER (PARTITION BY Equip ORDER BY Equip) RowNum
from #t) s
on t.Equip = s.Equip and t.Descr = s.Descr
select * from #t
And if possible, try to switch from table variable to temporary table
You can do what you want with a CTE:
WITH toupdate as (
SELECT t.*,
ROW_NUMBER() OVER (PARTITION BY t.Equip ORDER BY t.Equip) as seqnum
FROM #t t
UPDATE t
SET
Id = m.NextId + seqnum
FROM toupdate t INNER JOIN
#maxs m
ON m.Equip = t.Equip;
I'm not sure this is a good idea, though. It is better to use identity columns to identify each row. There are definitely some cases, though, where numbering within a group is useful as a secondary key (for instance, line items on an invoice).

Insert a table variable into a temp table with multiple columns (ID, Number, etc.)

I need to insert multiple Table variables into one temp table.
One of the table variables is:
DECLARE ##TempTable_Number TABLE (Number bigint)
insert into ##TempTable_Number (Number) values ('000000000000');
insert into ##TempTable_Number (Number) values ('100000000000');
This works for inserting just one table variable
select * into ##GlobalTempTable_1 from ##TempTable_Number
I have a couple more table variables like
DECLARE ##TempTable_ID TABLE (Number int)
insert into ##TempTable_ID (ID) values ('1');
insert into ##TempTable_ID (ID) values ('12');
etc...
I tried this to insert data from multiple table variables into one TempTable:
Select * into ####GlobalTempTable_1 From ##TempTable_ID, ##TempTable_Number;
The query goes to a continuous loop...
EDIT:
One of the table variables is:
DECLARE ##TempTable_Number TABLE (Number bigint, ID int)
insert into ##gvTempTable (Number) values ('21212321332332');
insert into ##gvTempTable (Number) values ('100000000000');
insert into ##gvTempTable (ID) values ('1');
insert into ##gvTempTable (ID) values ('12');
select * into ##GlobalTempTable from ##gvTempTable;
select * from ##GlobalTempTable;
This returns a kind of a cartesian product
Use UNION ALL:
SELECT ID
INTO ##GlobalTempTable_1
FROM ##TempTable_ID
UNION ALL
SELECT Number
FROM ##TempTable_Number;
LiveDemo
Select * into ####GlobalTempTable_1 From ##TempTable_ID, ##TempTable_Number;
The query goes to a continuous loop...
It is probably not loop but very long query. Keep in mind that you do Cartesian product.
So your query is the same as:
SELECT *
INTO ##GlobalTempTable_1
FROM ##TempTable_ID
CROSS JOIN ##TempTable_Number;
And the result is NxM records where N is number of records in first table and M in the second.
Try like this,
DECLARE #TempTable TABLE (
ID INT
,Number BIGINT
)
INSERT INTO #TempTable (Number)
VALUES ('21212321332332');
INSERT INTO #TempTable (Number)
VALUES ('100000000000');
INSERT INTO #TempTable (ID)
VALUES ('1');
INSERT INTO #TempTable (ID)
VALUES ('12');
--select * into #GlobalTempTable from ##gvTempTable;
--select * from ##GlobalTempTable;
SELECT *
FROM #TempTable
SELECT A.ID
,B.Number
FROM (
SELECT ID
,ROW_NUMBER() OVER (
ORDER BY ID
) TempId
FROM #TempTable
WHERE id IS NOT NULL
) A
INNER JOIN (
SELECT number
,ROW_NUMBER() OVER (
ORDER BY id
) TempId
FROM #TempTable
WHERE number IS NOT NULL
) B ON A.TempId = B.TempId

Deleting records that are similar with previous one SQL Server

I am looking for a query which fetches me the data that is different compared to the previous row,
A sample code (with table creation and data)
create table #temp
(id int, eid int, name char(10),estid int, ecid int, epid int, etc char(5) )
insert into #temp values (1,1,'a',1,1,1,'a')
insert into #temp values (2,1,'a',1,1,1,'a')
insert into #temp values (3,1,'a',2,1,1,'a')
insert into #temp values (4,1,'a',1,1,1,'a')
insert into #temp values (5,1,'a',1,1,1,'a')
insert into #temp values (6,1,'a',1,2,1,'a')
insert into #temp values (7,1,'a',1,1,1,'a')
insert into #temp values (8,1,'a',2,1,1,'a')
insert into #temp values (9,1,'a',1,1,1,'a')
insert into #temp values (10,1,'a',1,1,1,'a')
insert into #temp values (11,2,'a',1,1,1,'a')
insert into #temp values (12,2,'a',1,1,1,'a')
insert into #temp values (13,2,'a',2,1,1,'a')
insert into #temp values (14,2,'a',1,1,1,'a')
insert into #temp values (15,2,'a',1,1,1,'a')
insert into #temp values (16,2,'a',1,2,1,'a')
insert into #temp values (17,2,'a',1,1,1,'a')
insert into #temp values (18,2,'a',2,1,1,'a')
insert into #temp values (19,2,'a',1,1,1,'a')
insert into #temp values (20,2,'a',1,1,1,'a')
I tried with some ways of getting the data as the way that i expected
SELECT * INTo #Temp_Final
FROM #temp
WHERE #temp.%%physloc%%
NOT IN (SELECT Min(b.%%physloc%%)
FROM #temp b
GROUP BY eid,name,estid,ecid,epid,etc)
ORDER BY id
SELECT * FROM #temp WHERE id not in (SELECT id FROM #Temp_Final) ORDER BY id
But i wasn't getting the result as i expected...
This is how the result needs to be
select * from #temp where id in (1,3,4,6,7,8,9,11,13,14,16,17,18,19)
You can do this with a simple self-join and appropriate comparison:
select t.*
from #temp t left outer join
#temp tprev
on t.id = tprev.id + 1
where tprev.id is null or
t.name <> tprev.name or
t.estid <> tprev.estid or
t.ecid <> tprev.ecid or
t.epid <> tprev.epid or
t.etc <> tprev.etc;
This assumes that the ids are sequential with no gaps. If the ids are not, you can get the previous id using a correlated subquery or the lag() function.
Your title says "delete" but the question seems to just want the list of such rows. You can phrase this as a delete query if you need to.
For SQL Server 2012 (SQL Fiddle)
WITH CTE
AS (SELECT *,
LAG(eid) OVER (ORDER BY id) AS prev_eid,
LAG(name) OVER (ORDER BY id) AS prev_name,
LAG(estid) OVER (ORDER BY id) AS prev_estid,
LAG(ecid) OVER (ORDER BY id) AS prev_ecid,
LAG(epid) OVER (ORDER BY id) AS prev_epid,
LAG(etc) OVER (ORDER BY id) AS prev_etc
FROM #temp)
DELETE FROM CTE
WHERE EXISTS (SELECT eid,
name,
estid,
ecid,
epid,
etc
INTERSECT
SELECT prev_eid,
prev_name,
prev_estid,
prev_ecid,
prev_epid,
prev_etc)
select
t.id,
t.eid,
t.name,
t.estid,
t.ecid,
t.epid,
t.etc
from #temp t
left join #temp d
on d.id = t.id-1
and d.eid = t.eid
and d.name = t.name
and d.estid = t.estid
and d.ecid = t.ecid
and d.epid = t.epid
and d.etc = t.etc
where d.id is null

CTE to build hierarchy from source table

I am trying to build a CTE or just some query that takes hierarchial data from one table and insert it into another table that might have a different index. I thought this would be simple, but for some reason I'm getting stuck. I can't get the output to read correctly on the 'RequiredID' with the newly seeded index. Btw I'm working in SQL Server 2012.
Below I've provided proof of concept code to demonstrate what I'm going for. The actual SQL is more complex, but this illustrates the point.
This is what I've got so far:
DECLARE #Table TABLE (ID INT, Code NVARCHAR(50), RequiredID INT);
INSERT INTO #Table (ID, Code, RequiredID) VALUES
(1, 'Physics', NULL),
(2, 'Advanced Physics', 1),
(3, 'Nuke', 2),
(4, 'Health', NULL);
DECLARE #DefaultSeed TABLE (ID INT, Code NVARCHAR(50), RequiredID INT);
WITH hierarchy
AS (
--anchor
SELECT t.ID , t.Code , t.RequiredID
FROM #Table AS t
WHERE t.RequiredID IS NULL
UNION ALL
--recursive
SELECT t.ID
, t.Code
, h.ID
FROM hierarchy AS h
JOIN #Table AS t
ON t.RequiredID = h.ID
)
INSERT INTO #DefaultSeed (ID, Code, RequiredID)
SELECT ID
, Code
, RequiredID
FROM hierarchy
OPTION (MAXRECURSION 10)
DECLARE #NewSeed TABLE (ID INT IDENTITY(10, 1), Code NVARCHAR(50), RequiredID INT)
--this is where I get stuck - I can't get the requiredID to read like below
INSERT INTO #NewSeed (Code, RequiredID)
SELECT Code, RequiredID
FROM #DefaultSeed
--I'm trying to get #NewSeed should read like the following...
[ID] [Code] [RequiredID]
10....Physics..........NULL
11....Health...........NULL
12....AdvancedPhysics..10
13....Nuke.............12
SELECT *
FROM #NewSeed
Any help would be greatly appreciated!
You can use OUTPUT in combination with Merge to get a Mapping from ID's to new ID's.
The essential part:
--this is where you got stuck
Declare #MapIds Table (aOldID int,aNewID int)
;MERGE INTO #NewSeed AS TargetTable
Using #DefaultSeed as Source on 1=0
WHEN NOT MATCHED then
Insert (Code,RequiredID)
Values
(Source.Code,Source.RequiredID)
OUTPUT Source.ID ,inserted.ID into #MapIds;
Update #NewSeed Set RequiredID=aNewID
from #MapIds
Where RequiredID=aOldID
and the whole example:
DECLARE #Table TABLE (ID INT, Code NVARCHAR(50), RequiredID INT);
INSERT INTO #Table (ID, Code, RequiredID) VALUES
(1, 'Physics', NULL),
(2, 'Advanced Physics', 1),
(3, 'Nuke', 2),
(4, 'Health', NULL);
DECLARE #DefaultSeed TABLE (ID INT, Code NVARCHAR(50), RequiredID INT);
WITH hierarchy
AS (
--anchor
SELECT t.ID , t.Code , t.RequiredID
FROM #Table AS t
WHERE t.RequiredID IS NULL
UNION ALL
--recursive
SELECT t.ID
, t.Code
, h.ID
FROM hierarchy AS h
JOIN #Table AS t
ON t.RequiredID = h.ID
)
INSERT INTO #DefaultSeed (ID, Code, RequiredID)
SELECT ID
, Code
, RequiredID
FROM hierarchy
OPTION (MAXRECURSION 10)
DECLARE #NewSeed TABLE (ID INT IDENTITY(10, 1), Code NVARCHAR(50), RequiredID INT)
Declare #MapIds Table (aOldID int,aNewID int)
;MERGE INTO #NewSeed AS TargetTable
Using #DefaultSeed as Source on 1=0
WHEN NOT MATCHED then
Insert (Code,RequiredID)
Values
(Source.Code,Source.RequiredID)
OUTPUT Source.ID ,inserted.ID into #MapIds;
Update #NewSeed Set RequiredID=aNewID
from #MapIds
Where RequiredID=aOldID
/*
--#NewSeed should read like the following...
[ID] [Code] [RequiredID]
10....Physics..........NULL
11....Health...........NULL
12....AdvancedPhysics..10
13....Nuke.............12
*/
SELECT *
FROM #NewSeed

SQL Server Simple Group by query

I have a simple problem , Although i believe its simple , am not able to figure out the same.
Consider i have the below table with exactly same data as given below :
CREATE TABLE #temp
(
link varchar(255),
number INT,
fname varchar(255)
)
insert into #temp VALUES ('abc',1,'f1')
insert into #temp VALUES ('abc',2,'f2')
insert into #temp VALUES ('abc',3,'f3')
insert into #temp VALUES ('abc',4,'f6')
insert into #temp VALUES ('abc',10,'f100')
insert into #temp VALUES ('abe',-1,'f0')
insert into #temp VALUES ('abe',1,'f1')
insert into #temp VALUES ('abe',2,'f2')
insert into #temp VALUES ('abe',3,'f3')
insert into #temp VALUES ('abe',4,'f6')
insert into #temp VALUES ('abe',20,'f200')
insert into #temp VALUES ('cbe',-1,'f0')
insert into #temp VALUES ('cbe',1,'f1')
insert into #temp VALUES ('cbe',2,'f2')
insert into #temp VALUES ('cbe',3,'f3')
Now for a given link , i need to get the max 'number' and the corresponding 'fname' which has the max 'number' for the given 'link'.
1)Ex : if link is 'abc' , output should be
abc, 10, f100
2)Ex : if link if 'abe' , Output should be
abe, 20, f200
3)Now link can be also given as a pattern , like (link like 'ab%') , so output should be
abc, 10, f100
abe, 20, f200
4)if (link like 'cb%') , so output should be
cbe, 3, f3
Any help in writing this group by query. I have a solution using CAST and string concat like below , but that seems to be in-efficient.
select link,number,fname from #temp
where link like 'ab%' and link+'_'+CAST(number AS varchar(255))
in (select link+'_'+CAST(MAX(number) AS varchar(255)) from #temp
group by link)
Thanks..
Using a self join:
SELECT x.link,
x.number,
x.fname
FROM #temp x
JOIN (SELECT t.link,
MAX(t.number) AS max_number
FROM #temp t
GROUP BY t.link) y ON y.link = x.link
AND y.max_number = x.number
Using a CTE and ROW_NUMBER (SQL Server 2005+):
WITH cte AS (
SELECT x.link,
x.number,
x.fname,
ROW_NUMBER() OVER(PARTITION BY x.link
ORDER BY x.number DESC) rank
FROM #temp x)
SELECT c.link,
c.number,
c.fname
FROM cte c
WHERE c.rank = 1