How to select parents and children rows in SQL Server - sql

I want to select all itens related to user_id 53 (parents and children) from the following table. It should be: 1, 2, 4, 8, 9.
my_table
--------------------------------------------
id parent_id user_id sequence depth
--------------------------------------------
1 null 50 1 1
2 1 52 1.2 2
3 1 52 1.3 2
4 2 53 1.2.4 3
5 2 52 1.2.5 3
6 3 52 1.3.6 3
7 3 51 1.3.7 3
8 4 51 1.2.4.8 4
9 4 51 1.2.4.9 4
With CTE I could select all children or parents, but I could'nt select children and parents with just one query. Below is the cte I'm using to select children.
Item and children
with cte as (
select t.id, t.parent_id, t.user_id
from my_table t
where t.user_id=53
union all
select t.id, t.parent_id, t.user_id
from my_table t
inner join cte c on (c.parent_id=t.id)
)
select t.* from cte t;
Item and parents
with cte as (
select t.id, t.parent_id, t.user_id
from my_table t
where t.user_id=53
union all
select t.id, t.parent_id, t.user_id
from my_table t
inner join cte c on (c.id=t.parent_id)
)
select t.* from cte t;
Thanks.

It is very convenient that you have the sequences. The parents have a sequence matching an initial subset of the one you are looking for. The same is true for the children, but in reverse.
The following comes close to what you want:
select mt.*
from (select sequence from my_table where USER_ID = 53) theone join
my_table mt
on mt.sequence like theone.sequence+ '%' or
theone.sequence like mt.sequence + '%'
However, you have to be careful with 10 matching 1, for instance. So, let's add an additional period where appropriate:
select mt.*
from (select sequence from my_table where USER_ID = 53) theone join
my_table mt
on mt.sequence like theone.sequence+ '.%' or
theone.sequence like mt.sequence + '.%'

The problem here is, that you want a recursive SELECT. A DBMS is not made for such a query. But it's of course possible.
SELECT t1.* FROM Table1 t1 WHERE user_id = 53
UNION ALL
SELECT t2.* FROM Table1 t1, Table1 t2
WHERE
(t1.id = t2.parent_id OR t1.parent_id = t2.id) AND t1.user_id = 53
This query would give you each 1st degree relative.
For a recursive SELECT, have a look here: Recursive select in SQL
Solution for your query here: http://sqlfiddle.com/#!6/e1542/10/0
with cte as (
select t.id, t.parent_id, t.user_id
from Table1 t
where t.user_id=53
),
cte1 as (
select t.id, t.parent_id, t.user_id
from cte t
union all
select t.id, t.parent_id, t.user_id
from Table1 t
inner join cte1 c on (c.parent_id=t.id)
),
cte2 as (
select t.id, t.parent_id, t.user_id
from cte t
union all
select t.id, t.parent_id, t.user_id
from Table1 t
inner join cte2 c on (c.id=t.parent_id)
)
select t.* from cte1 t
UNION
select t.* from cte2 t

declare #Rownumber int;
CREATE TABLE #GetSeqUser (
RowNumber int,
UserId int,
)
INSERT INTO #GetSeqUser Select ROW_NUMBER() OVER(ORDER BY EmpId ASC) AS ROWNUMBER, EmpId FROM dbo.TreeSystem
set #Rownumber = (select ROWNUMBER from #GetSeqUser where UserId = 3 )
set #Rownumber = #Rownumber + 1
select UserId from #GetSeqUser where ROWNUMBER = #Rownumber

declare #Rownumber int;
CREATE TABLE #GetSeqUser (
RowNumber int,
UserId int,
)
INSERT INTO #GetSeqUser Select ROW_NUMBER() OVER(ORDER BY EmpId ASC) AS ROWNUMBER, EmpId FROM dbo.TreeSystem
set #Rownumber = (select ROWNUMBER from #GetSeqUser where UserId = 3 )
set #Rownumber = #Rownumber + 1
select UserId from #GetSeqUser where ROWNUMBER = #Rownumber

Related

unable to swap rows in sql

I have the below table where names will be inserted first. Then I will get IDs where I need to map with names
ID NAME
null Test1
null Test2
1 null
2 null
I need the result like
ID NAME
1 Test1
2 Test2
I tried below query but it doesn't work for me
select t1.ID , t2.Name from table1 T1 join table1 t2 on T1.id = t2.id
According to screen short you are working with SQL Server , you could try cte expression which may help you
;with cte as
(
select max(id) id, row_number() over (order by (select 1)) rn from
(
select *, rank() over(order by id) rnk from table
) a
group by a.rnk
having max(id) is not null
), cte1 as
(
select max(name) name, row_number() over (order by (select 1)) rn from
(
select *, rank() over(order by name) rnk from table
) a
group by a.rnk
having max(name) is not null
)
select c.id, c1.name from cte c
join cte1 c1 on c1.rn = c.rn
Result :
id name
1 test1
2 test2

SQL Self Recursive Join with Grouping

In SQL Server, I have the following table:
Name New_Name
---------------
A B
B C
C D
G H
H I
Z B
I want to create a new table that links all the names that are related to a single new groupID
GroupID Name
-------------
1 A
1 B
1 C
1 D
1 Z
2 G
2 H
2 I
I'm a bit stuck on this can can't figure out how to do it apart from a bunch of joins. But I would like to do it properly.
Edited the question to allow grouping from two different start points, A and Z into one group.
Since you've changed the question, I'm updating the answer. Please note that answer is same in sense of logical structure. All it does differently is that instead of going from G to I in calculating levels, answer now calculates from I to G.
Working demo link
;with cte as
(
select
t1.Name as Name, row_number() over (order by t1.Name) r,
t1.New_Name as New_Name,
1 as level
from yt t1 left join yt t2
on t1.New_Name=t2.name
where t2.name is null
union all
select
yt.Name as Name, r,
yt.New_Name as New_Name,
c.level+1 as level
from cte c join yt
on yt.New_Name=c.Name
),
cte2 as
(
select r as group_id, Name from cte
union
select c1.r as group_id, c1.New_name as Name from cte c1
where level = (select min(level) from cte c2 where c2.r=c1.r)
)
select * from cte2;
Below is old answer.
You can try below CTE based query:
create table yt (Name varchar(10), New_Name varchar(10));
insert into yt values
('A','B'),
('B','C'),
('C','D'),
('G','H'),
('H','I');
;with cte as
(
select
t1.Name as Name, row_number() over (order by t1.Name) r,
t1.New_Name as New_Name,
1 as level
from yt t1 left join yt t2
on t1.Name=t2.New_name
where t2.new_name is null
union all
select
yt.Name as Name, r,
yt.New_Name as New_Name,
c.level+1 as level
from cte c join yt
on yt.Name=c.New_Name
),
cte2 as
(
select r as group_id, Name from cte
union
select c1.r as group_id, c1.New_name as Name from cte c1
where level = (select max(level) from cte c2 where c2.r=c1.r)
)
select * from cte2;
see working demo
Little bit complex but working.
DECLARE #T TABLE (Name VARCHAR(2), New_Name VARCHAR(2))
INSERT INTO #T
VALUES
('A','B'),
('B','C'),
('C','D'),
('G','H'),
('H','I'),
('Z','B')
;WITH CTE AS
(
SELECT * , RN = ROW_NUMBER() OVER(ORDER BY Name) FROM #T
)
,CTE2 AS (SELECT T1.RN, T1.Name Name1, T1.New_Name New_Name1,
X.Name Name2, X.New_Name New_Name2,
FLAG = CASE WHEN X.Name IS NULL THEN 1 ELSE 0 END
FROM CTE T1
OUTER APPLY (SELECT * FROM CTE T2 WHERE T2.RN > T1.RN
AND (T2.Name IN (T1.Name , T1.New_Name)
OR T2.New_Name IN (T1.Name , T1.New_Name)
)) AS X
)
,CTE3 AS (SELECT *,
GroupID = ROW_NUMBER() OVER (ORDER BY RN) -
ROW_NUMBER() OVER (PARTITION BY Flag ORDER BY RN) +1
FROM CTE2
)
SELECT
DISTINCT GroupID, Name
FROM
(SELECT * FROM CTE3 WHERE Name2 IS NOT NULL) SRC
UNPIVOT ( Name FOR COL IN ([Name1], [New_Name1], [Name2], [New_Name2])) UNPVT
Result
GroupID Name
-------------------- ----
1 A
1 B
1 C
1 D
1 Z
2 G
2 H
2 I

Can I delete multiple of nth rows in a single query from a table in SQL?

I want to delete multiple of 4 from my table which have thousands of record. How can I do it?
Ex:-
Table1
1 a
2 b
3 c
4 d
5 e
6 f
7 g
8 h
9 i
I want to delete every 4th row.
I don't want to use a loop or cursor.
Delete A from
(
Select *,Row_Number() Over(Order By Id) as RN from TableA
) A
where RN%4=0
SQL Fiddle Link
Try this...
delete from table_name where (col1 % 4) = 0
Use a CTE.
WITH cte AS (
SELECT t.*, ROW_NUMBER() OVER (ORDER BY t.rowfield) AS rank
FROM Table1 t)
SELECT rowfield, fielda
FROM cte
WHERE rank%4 != 0
Output
rowfield fielda
1 a
2 b
3 c
5 e
6 f
7 g
9 i
SQL Fiddle: http://sqlfiddle.com/#!6/c9540b/13/0
Once you are happy with the output use DELETE FROM.
This can use an index if one exists and uses numbers table
;with cte
as
(select n from numbers
where n%4=0
)
delete t
from table1 t
join
cte c
on c.n=t.id
Try this
DECLARE #nvalToDelete varchar(100)='4,7,3,8,9'-- just give values to delete from table
DECLARE #temp TABLE
(
valtodelete VARCHAR(100)
)
DECLARE #Deletetemp TABLE
(
valtodelete INT
)
INSERT INTO #temp
SELECT #nvalToDelete
INSERT INTO #Deletetemp
SELECT split.a.value('.', 'VARCHAR(1000)') AS valToDelete
FROM (SELECT Cast('<S>' + Replace(valtodelete, ',', '</S><S>')
+ '</S>' AS XML) AS valToDelete
FROM #temp) AS A
CROSS apply valtodelete.nodes('/S') AS Split(a)
DECLARE #Table1 TABLE
(
ID INT,
val varchar(10)
)
INSERT INTO #Table1
SELECT 1,'a' UNION ALL
SELECT 2,'b' UNION ALL
SELECT 3,'c' UNION ALL
SELECT 4,'d' UNION ALL
SELECT 5,'e' UNION ALL
SELECT 6,'f' UNION ALL
SELECT 7,'g' UNION ALL
SELECT 8,'h' UNION ALL
SELECT 9,'i'
SELECT *
FROM #Table1;
WITH cte
AS (SELECT *,
RN = Row_number()
OVER (
ORDER BY id )
FROM #Table1)
DELETE FROM #Table1
WHERE id IN(SELECT id FROM cte
WHERE rn IN (SELECT CASt(valToDelete AS INT) FROM #Deletetemp)
)
SELECT *
FROM #Table1

concatenate recursive cross join

I need to concatenate the name in a recursive cross join way. I don't know how to do this, I have tried a CTE using WITH RECURSIVE but no success.
I have a table like this:
group_id | name
---------------
13 | A
13 | B
19 | C
19 | D
31 | E
31 | F
31 | G
Desired output:
combinations
------------
ACE
ACF
ACG
ADE
ADF
ADG
BCE
BCF
BCG
BDE
BDF
BDG
Of course, the results should multiply if I add a 4th (or more) group.
Native Postgresql Syntax:
SqlFiddleDemo
WITH RECURSIVE cte1 AS
(
SELECT *, DENSE_RANK() OVER (ORDER BY group_id) AS rn
FROM mytable
),cte2 AS
(
SELECT
CAST(name AS VARCHAR(4000)) AS name,
rn
FROM cte1
WHERE rn = 1
UNION ALL
SELECT
CAST(CONCAT(c2.name,c1.name) AS VARCHAR(4000)) AS name
,c1.rn
FROM cte1 c1
JOIN cte2 c2
ON c1.rn = c2.rn + 1
)
SELECT name as combinations
FROM cte2
WHERE LENGTH(name) = (SELECT MAX(rn) FROM cte1)
ORDER BY name;
Before:
I hope if you don't mind that I use SQL Server Syntax:
Sample:
CREATE TABLE #mytable(
ID INTEGER NOT NULL
,TYPE VARCHAR(MAX) NOT NULL
);
INSERT INTO #mytable(ID,TYPE) VALUES (13,'A');
INSERT INTO #mytable(ID,TYPE) VALUES (13,'B');
INSERT INTO #mytable(ID,TYPE) VALUES (19,'C');
INSERT INTO #mytable(ID,TYPE) VALUES (19,'D');
INSERT INTO #mytable(ID,TYPE) VALUES (31,'E');
INSERT INTO #mytable(ID,TYPE) VALUES (31,'F');
INSERT INTO #mytable(ID,TYPE) VALUES (31,'G');
Main query:
WITH cte1 AS
(
SELECT *, rn = DENSE_RANK() OVER (ORDER BY ID)
FROM #mytable
),cte2 AS
(
SELECT
TYPE = CAST(TYPE AS VARCHAR(MAX)),
rn
FROM cte1
WHERE rn = 1
UNION ALL
SELECT
[Type] = CAST(CONCAT(c2.TYPE,c1.TYPE) AS VARCHAR(MAX))
,c1.rn
FROM cte1 c1
JOIN cte2 c2
ON c1.rn = c2.rn + 1
)
SELECT *
FROM cte2
WHERE LEN(Type) = (SELECT MAX(rn) FROM cte1)
ORDER BY Type;
LiveDemo
I've assumed that the order of "cross join" is dependent on ascending ID.
cte1 generate DENSE_RANK() because your IDs contain gaps
cte2 recursive part with CONCAT
main query just filter out required length and sort string
The recursive query is a bit simpler in Postgres:
WITH RECURSIVE t AS ( -- to produce gapless group numbers
SELECT dense_rank() OVER (ORDER BY group_id) AS grp, name
FROM tbl
)
, cte AS (
SELECT grp, name
FROM t
WHERE grp = 1
UNION ALL
SELECT t.grp, c.name || t.name
FROM cte c
JOIN t ON t.grp = c.grp + 1
)
SELECT name AS combi
FROM cte
WHERE grp = (SELECT max(grp) FROM t)
ORDER BY 1;
The basic logic is the same as in the SQL Server version provided by #lad2025, I added a couple of minor improvements.
Or you can use a simple version if your maximum number of groups is not too big (can't be very big, really, since the result set grows exponentially). For a maximum of 5 groups:
WITH t AS ( -- to produce gapless group numbers
SELECT dense_rank() OVER (ORDER BY group_id) AS grp, name AS n
FROM tbl
)
SELECT concat(t1.n, t2.n, t3.n, t4.n, t5.n) AS combi
FROM (SELECT n FROM t WHERE grp = 1) t1
LEFT JOIN (SELECT n FROM t WHERE grp = 2) t2 ON true
LEFT JOIN (SELECT n FROM t WHERE grp = 3) t3 ON true
LEFT JOIN (SELECT n FROM t WHERE grp = 4) t4 ON true
LEFT JOIN (SELECT n FROM t WHERE grp = 5) t5 ON true
ORDER BY 1;
Probably faster for few groups. LEFT JOIN .. ON true makes this work even if higher levels are missing. concat() ignores NULL values. Test with EXPLAIN ANALYZE to be sure.
SQL Fiddle showing both.

SQL create table with integers

I want to have in a database a table with single column with integers (1,2,...) since it can be helpful for certain joins.
I came up with a solution using a loop. Is there a more efficient way of creating such a table?
My solution
CREATE TABLE #NUM
(
NUM int
)
DECLARE #i int=1
WHILE #i<10000
BEGIN
INSERT INTO #Temp
SELECT #i
SET #i = #i + 1
END
I will do this using a tally table
;WITH e1(n) AS
(
SELECT 1 UNION ALL SELECT 1 UNION ALL SELECT 1 UNION ALL
SELECT 1 UNION ALL SELECT 1 UNION ALL SELECT 1 UNION ALL
SELECT 1 UNION ALL SELECT 1 UNION ALL SELECT 1 UNION ALL SELECT 1
), -- 10
e2(n) AS (SELECT 1 FROM e1 CROSS JOIN e1 AS b), -- 10*10
e3(n) AS (SELECT 1 FROM e2 CROSS JOIN e2 AS b) -- 100*100
INSERT INTO #Temp
SELECT ROW_NUMBER() OVER (ORDER BY n) FROM e3 ORDER BY n;
check here for more info
SELECT
row_number() over (order by message_id) num
into #Table
from sys.messages
This is the same principle as the answer by NoDisplayName, but I think it is a little cleaner (my opinion):
;WITH TBL(ROW_NUM) AS (
SELECT 1 AS ROW_NUM
UNION ALL
SELECT ROW_NUM+1
FROM TBL
WHERE ROW_NUM < 100
)
SELECT ROW_NUMBER() OVER (ORDER BY T1.ROW_NUM) AS ROW_NUM
FROM TBL T1
JOIN TBL T2 ON 1=1
JOIN TBL T3 ON 1=1