I have this table:
Id |Name |ParentId
1 |John |Null
2 |Oscar |1
3 |Peter |2
4 |Abbey |3
5 |Adrian |4
6 |Barbara |5
and i want to make a select that will give me a new column that gets the previous Name with by the parentId to make a listName (Order by ParentID).
the final result in this example would be this:
Id |Name |ParentId | List
1 |John |Null | John
2 |Oscar |1 | John-Oscar
3 |Peter |2 | John-Oscar-Peter
4 |Abbey |3 | John-Oscar-Peter-Abbey
5 |Adrian |4 | John-Oscar-Peter-Abbey-Adrian
6 |Barbara |5 | John-Oscar-Peter-Abbey-Adrian-Barbara
Thnks for all the help!
You can use a recursive CTE to produce the desired result:
declare #t table (Id int, Name varchar(20), ParentId int)
insert #t values
( 1 ,'John' ,Null ),
( 2 ,'Oscar' ,1 ),
( 3 ,'Peter' ,2 ),
( 4 ,'Abbey' ,3 ),
( 5 ,'Adrian' ,4 ),
( 6 ,'Barbara' ,5 )
;with x as (
select *, cast(name as varchar(1000)) as list from #t where parentid is null
union all
select t.id, t.name, t.parentid, cast(x.list+'-'+t.name as varchar(1000)) from #t t join x on t.parentid = x.id
)
select * from x
This also works for multiple roots, of course.
This is same as concatenate columns to rows
select id,name,pid,
stuff((select '-'+name from yourtable n2 where n2.id<=n1.id for xml path('')),1,1,'') b
from yourtable n1
Related
I have MS SQL database table like this
TableA
+----+-----------+--------+
|ID | Table2_FK | Value |
+----+-----------+--------+
|1 | 7 | X |
|2 | 7 | Y |
|3 | 8 | X |
|4 | 8 | Z |
|5 | 9 | W |
|6 | 9 | M |
|5 | 10 | X |
|6 | 10 | Z |
+----+-----------+--------+
I want to make query to get list of Table2_FKs if I pass X and Z in query for Values. In this example 8 and 10 is the result
It can be more than 2 values
You can do this with group by and having:
select table2_fk
from t
where value in ('X', 'Z')
group by table2_fk
having count(*) = 2;
If the values can be duplicated for a key value, then use count(distinct value) = 2. The "2" is the number of values in the IN list.
Try this:
select distinct Table2_FK
from TableA
where value in ('X','Z');
You can use query as below:
Select distinct table2_fk from (
Select *, Ct = count(id) over (partition by table2_fk) from yourtable
) a
Where a.[Value] in ('X','Z') and a.Ct >= 2
you can use a query like below
select
distinct Table2_FK
from TableA a
where exists (
select 1 1 from TableA b where b.value ='X' and a.Table2_FK =b.Table2_FK
)
and exists (
select 1 1 from TableA c where c.value ='Z' and a.Table2_FK =c.Table2_FK
)
I have a parent child relation table as shown below:
ContractID ContractIdRef
---------- -------------
1 null
2 1
3 1
4 2
5 4
10 null
11 10
12 11
15 null
16 12
I want result like below:
ContractID ContractIdRef rw
----------- -------------- ---
1 null 1
2 1 1
3 1 1
4 2 1
5 4 1
10 null 10
11 10 10
12 11 10
15 null 15
16 12 10
In above result I want to specify each rows parent.
Thanks
As you mentioned in the TAGS Comman Table Expression is the way to go
;WITH REC_CTE
AS (SELECT [contractid],
[ContractIdRef],
[contractid] AS rw
FROM Yourtable
WHERE [contractidref] IS NULL
UNION ALL
SELECT T.[contractid],
T.[contractidref],
c.rw
FROM Yourtable AS T
INNER JOIN REC_CTE C
ON T.[contractidref] = c.[contractid]
WHERE T.[contractid] <> T.[contractidref])
SELECT [contractid],
[contractidref],
rw
FROM REC_CTE
ORDER BY [contractid]
Demo
Schema Setup
If object_id('tempdb.dbo.#Yourtable') is not null
DROP table #Yourtable
CREATE TABLE #Yourtable
([ContractID] INT, [ContractIdRef] INT);
Sample data
INSERT INTO #Yourtable
([ContractID], [ContractIdRef])
VALUES
('1', NULL),
('2', '1'),
('3', '1'),
('4', '2'),
('5', '4'),
('10', NULL),
('11', '10'),
('12', '11'),
('15', NULL),
('16', '12');
Query
;WITH REC_CTE
AS (SELECT [ContractID],
[ContractIdRef] as [ContractIdRef],
[ContractID] AS rw
FROM #Yourtable where [ContractIdRef] is null
UNION ALL
SELECT T.[ContractID],
T.[ContractIdRef],
c.rw
FROM #Yourtable AS T
INNER JOIN REC_CTE c
ON T.[ContractIdRef] = c.[ContractID]
WHERE T.[ContractID] <> T.[ContractIdRef])
SELECT [ContractID],
[ContractIdRef],
rw
FROM REC_CTE
ORDER BY [ContractID]
Result
+-----------+-------------+----+
|ContractID |ContractIdRef| rw |
+-----------+-------------+----+
|1 |NULL | 1 |
|2 |1 | 1 |
|3 |1 | 1 |
|4 |2 | 1 |
|5 |4 | 1 |
|10 |NULL | 10 |
|11 |10 | 10 |
|12 |11 | 10 |
|15 |NULL | 15 |
|16 |12 | 10 |
+-----------+-------------+----+
with Q as(
select ContractID, ContractIdRef, ContractID as root
from childs
where ContractIdRef is null
union all
select C.ContractID, C.ContractIdRef, Q.root
from Q, childs C
where C.ContractIdRef=Q.ContractID
)
select * from Q
order by ContractID
Tested on MS SQL 2014.
For Postgresql need add word 'recursive' after 'with'. Test on sqlfiddle.com
For Oracle first line writed as with Q(ContractID,ContractIdRef,root).
Using MS SQL SERVER 2008
I have the following scenario below:
===================
| Table A |
===================
|id |desc |code
| 1 |ballpen |1010
| 2 |pencil |1010
| 3 |stabilo |1010
| 4 |pins |1011
| 5 |clips |1011
===================
| Table B |
===================
| id |code
| 1010 |AAA
| 1011 |BBB
| 1013 |CCC
What I need to accomplish is something below:
===================
| Table C |
===================
|id |desc |code |code 2
| 1 |ballpen |1010 |AAA
| 2 |pencil |1010 |AAA
| 3 |stabilo |1010 |AAA
| 4 |pins |1011 |BBB
| 5 |clips |1011 |BBB
Here's what I have so far, unfortunately the LEFT JOIN is causing the final query output to bloat
select *
from table a
left join table b on a.code = b.id
Result:
========
Table D
========
|id |desc |code |code2
|1 |ballpen |1010 |AAA
|1 |ballpen |1010 |AAA
|1 |ballpen |1010 |AAA
|2 |pencil |1010 |AAA
|2 |pencil |1010 |AAA
|2 |pencil |1010 |AAA
|3 |stabilo |1010 |AAA
|3 |stabilo |1010 |AAA
|3 |stabilo |1010 |AAA
|4 |pins |1011 |BBB
|4 |pins |1011 |BBB
|5 |clips |1011 |BBB
|5 |clips |1011 |BBB
Note:I think u have used same table name in join.let try using different table name
I have used Left Join it works..
create table #master
(
ID int,
descs varchar(15),
code int
)
create table #codes
(
ids int,
codes varchar(15)
)
insert into #master values(1,'ballpen',1010)
insert into #master values(2,'pencil',1010)
insert into #master values(3,'stabilo',1010)
insert into #master values(4,'pins',1011)
insert into #master values(5,'Clips',1011)
insert into #codes values(1010,'AAA')
insert into #codes values(1011,'BBB')
insert into #codes values(1013,'CCC')
select ID,descs,code,codes from #master a
left join #codes b
on a.code = b.ids
Output:
ID descs code codes
1 ballpen 1010 AAA
2 pencil 1010 AAA
3 stabilo 1010 AAA
4 pins 1011 BBB
5 Clips 1011 BBB
In case left join not works for you use Inner join:
select ID,descs,code,codes from #master a,#codes b
where a.code=b.ids
You should try a inner Join
SELECT
distinct a.id,a.desc1,a.code,b.code as code2
from a
inner join b
on a.code = b.id
I guess that table B contains not unique code, so you need to do distinct of the selected dataset
here the test:
--Create temp tables for data sample
--table A - unique ID
DECLARE #tableA AS TABLE
(
id INT ,
[DESC] VARCHAR(20) ,
code BIGINT
)
INSERT INTO #tableA
( id, [DESC], code )
VALUES ( 1, 'ballpen', 1010 ),
( 2, 'pencil', 1010 ),
( 3, 'stabilo', 1010 ),
( 4, 'pins', 1011 ),
( 5, 'clips', 1011 )
--table B not unique code
DECLARE #tableB AS TABLE ( id INT, code VARCHAR(10) )
INSERT INTO #tableB
( id, code )
VALUES ( 1010, 'AAA' ),
( 1011, 'BBB' ),
( 1013, 'CCC' ),
( 1010, 'AAA' ),
( 1011, 'BBB' ),
( 1013, 'CCC' ),
( 1010, 'AAA' ),
( 1011, 'BBB' ),
( 1013, 'CCC' )
------------------------------------------------------------------------------------------
--1 variant of the final query
SELECT DISTINCT
a.id ,
a.[DESC] ,
a.code ,
b.code AS [code 2]
FROM #tableA AS a
LEFT JOIN #tableB AS b ON a.code = b.id
--2 variant of the final query
SELECT *
FROM ( SELECT DISTINCT
a.id ,
a.[DESC] ,
a.code ,
b.code AS [code 2] ,
ROW_NUMBER() OVER ( PARTITION BY a.id ORDER BY a.id ) AS RN
FROM #tableA AS a
LEFT JOIN #tableB AS b ON a.code = b.id
) AS t
WHERE rn = 1
--3 variant of the final query
;WITH cte AS
( SELECT DISTINCT
a.id ,
a.[DESC] ,
a.code ,
b.code AS [code 2] ,
ROW_NUMBER() OVER ( PARTITION BY a.id ORDER BY a.id ) AS RN
FROM #tableA AS a
LEFT JOIN #tableB AS b ON a.code = b.id
)
SELECT *
FROM cte
WHERE rn = 1
--4 variant of the final query
SELECT TOP 1 WITH TIES
a.id ,
a.[DESC] ,
a.code ,
b.code AS [code 2]
FROM #tableA AS a
LEFT JOIN #tableB AS b ON a.code = b.id
ORDER BY ROW_NUMBER() OVER ( PARTITION BY a.id ORDER BY a.id )
below screenshot of the result:
I have a table structure like this (there are actually more levels):
------------------------------------------
|region1|region2|region3|region4|postcode|
|-------|-------|-------|-------|--------|
|a |x |i | |1 |
|a |y |i | |2 |
|a |y |j | |2 |
|a |z |k | |3 |
|b |u |m | |4 |
|b | |n | |4 |
|c | | | |5 |
|c |q | | |6 |
------------------------------------------
So for example, a => x => i and a => y => i are different places but both are in the same region1 a.
I want to know which region each postcode can cover.
For example, code 2 covers areas a => y => i and a => y => j, so the common ancestor for those are a => y.
Here is the desired output of the query run on the example:
------------------------------------------
|postcode|region1|region2|region3|region4|
|--------|-------|-------|-------|-------|
|1 |a |x |i | |
|2 |a |y | | |
|3 |a |z |k | |
|4 |b | | | |
|5 |c | | | |
|6 |c |q | | |
------------------------------------------
I don't really know how to attack this problem. I thought about partitioning by the postcode, but that still leaves the problem of finding the common ancestor within each partition...
This is a really messy solution, but it does seem to give the correct answer. It would no doubt need quite a bit of work to fit your actual requirement, but maybe this could help point you in some sort of direction!
-- Setup a test table
DECLARE #tbl AS TABLE(R1 NVARCHAR(10), R2 NVARCHAR(10), R3 NVARCHAR(10), R4 NVARCHAR(10), PC NVARCHAR(10));
INSERT INTO #tbl(R1,R2,R3,R4,PC) VALUES ('a','x','i',NULL,'1');
INSERT INTO #tbl(R1,R2,R3,R4,PC) VALUES ('a','y','i',NULL,'2');
INSERT INTO #tbl(R1,R2,R3,R4,PC) VALUES ('a','y','j',NULL,'2');
INSERT INTO #tbl(R1,R2,R3,R4,PC) VALUES ('a','z','k',NULL,'3');
INSERT INTO #tbl(R1,R2,R3,R4,PC) VALUES ('b','u','m',NULL,'4');
INSERT INTO #tbl(R1,R2,R3,R4,PC) VALUES ('b',NULL,'n',NULL,'4');
INSERT INTO #tbl(R1,R2,R3,R4,PC) VALUES ('c',NULL,NULL,NULL,'5');
INSERT INTO #tbl(R1,R2,R3,R4,PC) VALUES ('c','q',NULL,NULL,'6');
-- Calculate the result:
SELECT
PC,
CASE WHEN LVL1 = 1 THEN R1 ELSE NULL END AS R1,
CASE WHEN LVL2 = 1 THEN R2 ELSE NULL END AS R2,
CASE WHEN LVL3 = 1 THEN R3 ELSE NULL END AS R3,
CASE WHEN LVL4 = 1 THEN R4 ELSE NULL END AS R4
FROM
(
SELECT
PC,
MAX(R1) AS R1,
MAX(R2) AS R2,
MAX(R3) AS R3,
MAX(R4) AS R4,
COUNT(DISTINCT ISNULL(R1,'.')) AS LVL1,
COUNT(DISTINCT ISNULL(R1,'.') + ISNULL(R2,'.')) AS LVL2,
COUNT(DISTINCT ISNULL(R1,'.') + ISNULL(R2,'.') + ISNULL(R3,'.')) AS LVL3,
COUNT(DISTINCT ISNULL(R1,'.') + ISNULL(R2,'.') + ISNULL(R3,'.') + ISNULL(R4,'.')) AS LVL4
FROM #tbl
GROUP BY PC
) A
The end result matches the table in the question.
This question rather intrigued me and I came up with an alternative, which you might find useful:
-- Setup test table
DECLARE #InputTable TABLE (region1 varchar(2), region2 varchar(2), region3 varchar(2), region4 varchar(2), postcode varchar(2))
INSERT INTO #InputTable (region1, region2, region3, region4, postcode)
SELECT 'a','x','i',null,'1'
UNION ALL SELECT 'a','y','i',NULL,'2'
UNION ALL SELECT 'a','y','j',NULL,'2'
UNION ALL SELECT 'a','z','k',NULL,'3'
UNION ALL SELECT 'b','u','m',NULL,'4'
UNION ALL SELECT 'b',NULL,'n',NULL,'4'
UNION ALL SELECT 'c',NULL,NULL,NULL,'5'
UNION ALL SELECT 'c','q',NULL,NULL,'6'
-- Find the common ancestors
;with totals as (
select postcode, count(*) as postcodeCount from #InputTable group by postcode
)
, region4group as (
select postcode, region1, region2, region3, region4 from #InputTable in1
group by postcode, region1, region2, region3, region4 having count(*)=(select postCodeCount from totals where totals.postcode=in1.postcode)
)
, region3group as (
select * from region4group
union
select in1.postcode, in1.region1, in1.region2, in1.region3, null from #InputTable in1
left outer join region4group on region4group.postcode=in1.postcode
where region4group.postcode is null
group by in1.postcode, in1.region1, in1.region2, in1.region3
having count(*)=(select postCodeCount from totals where totals.postcode=in1.postcode)
)
, region2group as (
select * from region3group
union
select in1.postcode, in1.region1, in1.region2, null, null from #InputTable in1
left outer join region3group on region3group.postcode=in1.postcode
where region3group.postcode is null
group by in1.postcode, in1.region1, in1.region2
having count(*)=(select postCodeCount from totals where totals.postcode=in1.postcode)
)
, commonancestors as (
select * from region2group
union
select in1.postcode, in1.region1, null, null, null from #InputTable in1
left outer join region2group on region2group.postcode=in1.postcode
where region2group.postcode is null
group by in1.postcode, in1.region1
having count(*)=(select postCodeCount from totals where totals.postcode=in1.postcode)
)
select * from commonancestors
Hi I have the following requirement
In Table A
CRD | RNo | J_NAME
-------------------------
DOS1 |1 | NULL
DOS2 |2 | Name 1
DOS3 |3 | Name 2
DOS4 |4 | Name 3
DOS5 |5 | Name 1
DOS6 |6 | Name 1
DOS7 |7 | Name 4
DOS8 |8 | Name 2
Out put should be
CRD | RNo | J_NAME
-------------------------
DOS1 |1 | NULL
DOS2 |2 | A
DOS3 |3 | B
DOS4 |4 | C
DOS5 |5 | A
DOS6 |6 | A
DOS7 |7 | D
DOS8 |8 | B
Null allays should be null, If the name already exist in the target table then It will be add the same name eg: J_Name = A and B, if the source value is not in the target table then it will get a new entry from the list.
Ho I can achieve this?
You can try somthing like this:-
SELECT CRD, RNo,
CASE WHEN J_NAME ='Name 1' THEN 'A'
WHEN J_NAME ='Name 2' THEN 'B'
WHEN J_NAME ='Name 3' THEN 'C'
WHEN J_NAME ='Name 4' THEN 'D'
ELSE 'NULL' END AS J_NAME
FROM TAB;
If you're sure you won't run out of letters (since you're not telling how to generate values beyond Z), you could just dense_rank your names and give them a letter corresponding to the rank;
WITH cte AS (
SELECT * FROM mytable UNION ALL SELECT '', 0, NULL FROM DUAL
)
SELECT crd, rno, CASE WHEN j_name IS NULL THEN NULL ELSE CHR(calc+63) END j_name
FROM (
SELECT crd, rno, j_name, DENSE_RANK() OVER (ORDER BY j_name NULLS FIRST) calc
FROM cte
)
WHERE rno > 0 ORDER BY rno;
It basically takes the table contents, adds a row with a null J_NAME value to make sure there's a rank for NULL, and uses DENSE_RANK() on the resulting j_names to get a value to generate the letter from.
An SQLfiddle to test with.