I have a table like by following..
ID CustId CustName Status
1 a1 A NULL
2 a1 A NULL
3 a2 B NULL
4 a3 B NULL
5 a4 C NULL
6 a4 C NULL
7 a5 D NULL
8 a6 E NULL
I want to update the status = 2 when custid occurs 2nd time and I need output following like this...
ID CustId CustName Status
1 a1 A 1
2 a1 A 2
3 a2 B 1
4 a3 B 1
5 a4 C 1
6 a4 C 2
7 a4 D 2
8 a6 E 1
Now I am using the following query to update the status
update #tablename
set status= 2
where Custid in
(
select * from
(
select Custid
from #tablename
group by Custid
having count(*)> 1
) a
)
but the above query is updating the status=2 when count(custid)>1
I don't want to update first row..
CREATE TABLE #UserCompany
([ID] int, [CustId] varchar(2), [CustName] varchar(1), [Status] int)
;
INSERT INTO #UserCompany
([ID], [CustId], [CustName], [Status])
VALUES
(1, 'a1', 'A', null),
(2, 'a1', 'A', null),
(3, 'a2', 'B', null),
(4, 'a3', 'B', null),
(5, 'a4', 'C', null),
(6, 'a4', 'C', null),
(7, 'a4', 'D', null),
(8, 'a6', 'E', null)
;
select *,row_number() over (partition by [CustId] order by [ID]) as rn from #UserCompany
with cte as
(
select *,row_number() over (partition by [CustId] order by [ID]) as rn from #UserCompany)
update cte
set [Status]=case when rn > 1 then 2 else 1 end
or
UPDATE b
SET b.[Status] = a.[Status]
FROM #UserCompany a
INNER JOIN (select *,row_number() over (partition by [CustId] order by [ID]) as rn from #UserCompany) b
ON a.id = b.id
Intro
Easiest way to solve this would be with a subquery.
So for every row, you generate a subquery returning only one value.
This subquery will basically count the number of records ( for that customer id), that have an ID >= the current customer id.
Code
UPDATE UserCompany
SET
UserCompany.status = temp.status
FROM usercompany AS u
INNER JOIN
( SELECT ID,
CustID,
CustName,
(SELECT count(*)
FROM UserCompany t
WHERE t.custId = u.custId
AND t.ID <= u.id ) AS status
FROM UserCompany u ) AS TEMP ON temp.ID = u.ID;
select * from UserCompany;
Update 1: Only update records where previous same cust instance-count >= 2
UPDATE UserCompany
SET
UserCompany.status = temp.status
FROM usercompany AS u
INNER JOIN
( SELECT ID,
CustID,
CustName,
(SELECT count(*)
FROM UserCompany t
WHERE t.custId = u.custId
AND t.ID <= u.id ) AS status
FROM UserCompany u ) AS TEMP ON temp.ID = u.ID AND TEMP.STATUS >=2;
select * from UserCompany;
Update 2: Same as Update 1, but set 2 instead of actual count
(same as above but inserts 2 instead of real count)
UPDATE UserCompany
SET
UserCompany.status = temp.insert_status
FROM usercompany AS u
INNER JOIN
( SELECT ID,
CustID,
CustName,
2 as insert_status,
(SELECT count(*)
FROM UserCompany t
WHERE t.custId = u.custId
AND t.ID <= u.id ) AS status
FROM UserCompany u ) AS TEMP ON temp.ID = u.ID AND TEMP.STATUS >=2;
select * from UserCompany;
Related
I have two tables:
TableA
ID Name
-- ----
1 aaa
2 bbb
3 ccc
4 ddd
TableB
ID Name
-- --------
3 WWXXYYZZ
I want to select from both tables, but skip the rows which exist in TableB. The result should look like this:
ID Name
-- --------
1 aaa
2 bbb
3 WWXXYYZZ
4 ddd
I have tried union and join but did not figure out how to achieve this.
-- Did not work
select *
from TableA
union
select *
from TableB
-- Did not work
select *
from
(
select *
from TableA
) x
join
(
select *
from TableB
) y
on x.ID = y.ID
You could left join b on to a, and use coalesce to prefer b's rows:
SELECT a.id, COALESCE(b.name, a.name) AS name
FROM a
LEFT JOIN b ON a.id = b.id
You can do:
select a.id, coalesce(b.name, a.name)
from a left join b on a.id = b.id
One method is union all:
select b.*
from b
union all
select a.*
from a
where not exists (select 1 from a where a.id = b.id);
You can also choose from a and override with values from b:
select a.id, coalesce(b.name, a.name) as name
from a left join
b
on a.id = b.id;
A more complex method uses ROW_NUMBER which might be necessary if your query is significantly more complex than shown. It also handled the case where a row exists in TableB but not TableA (which is not clear from your question).
DECLARE #TableA TABLE (id INT, [Name] VARCHAR(12));
DECLARE #TableB TABLE (id INT, [Name] VARCHAR(12));
INSERT INTO #TableA (id, [Name])
VALUES
(1, 'aaa'),
(2, 'bbb'),
(3, 'ccc'),
(4, 'ddd');
INSERT INTO #TableB (id, [Name])
VALUES
(3, 'WWXXYYZZ'),
(5, 'TTTGGG');
SELECT id, [Name]
FROM (
SELECT id, [Name]
, ROW_NUMBER() OVER (PARTITION BY id ORDER BY [Priority] DESC) Rn
FROM (
SELECT id, [Name], 0 [Priority]
FROM #TableA
UNION ALL
SELECT id, [Name], 1 [Priority]
FROM #TableB
) X
) Y
WHERE Rn = 1;
Returns:
1 aaa
2 bbb
3 WWXXYYZZ
4 ddd
5 TTTGGG
I am trying to build a deep recursive self-join query. Having the table like:
Id | ParentId
1 | NULL
2 | 1
3 | 1
4 | 2
5 | 3
6 | 8
7 | 9
For Id 1 my query should be fetching 1,2,3,4,5 since they are either the children of 1 or children of the children of 1. In the given example 6 and 7 should not be included in the query result.
I tried using CTE but I am getting tons of duplicates:
WITH CTE AS (
SELECT Id, ParentId
FROM dbo.Table
WHERE ParentId IS NULL
UNION ALL
SELECT t.Id, t.ParentId
FROM dbo.Table t
INNER JOIN CTE c ON t.ParentId = c.Id
)
SELECT * FROM CTE
Ideas?
You can try to use DISTINCT to filter duplicate rows.
;WITH CTE AS (
SELECT Id, ParentId
FROM T
WHERE ParentId IS NULL
UNION ALL
SELECT t.Id, t.ParentId
FROM T
INNER JOIN CTE c ON t.ParentId = c.Id
)
SELECT DISTINCT Id, ParentId
FROM CTE
Try the following query with CTE where you can set parentId by #parentID:
DECLARE #parentID INT = 1
;WITH cte AS
(
SELECT
t.ID
, t.ParentId
FROM #table t
),
cteParent AS
(
SELECT
t.ID
, t.ParentId
FROM #table t
WHERE t.ParentId IN (SELECT t1.ID FROM #table t1 WHERE T1.ParentId = #parentID)
)
SELECT
DISTINCT c1.ID
, c1.ParentId
FROM cte c1
INNER JOIN cte c2 ON c2.ParentId = c1.ID
UNION ALL
SELECT *
FROM cteParent
And the sample data:
DECLARE #table TABLE
(
ID INT
, ParentId INT
)
INSERT INTO #table
(
ID,
ParentId
)
VALUES
(1, NULL )
, (2, 1 )
, (3, 1 )
, (4, 2 )
, (5, 3 )
, (6, 8 )
, (7, 9 )
OUTPUT:
ID ParentId
1 NULL
2 1
3 1
4 2
5 3
I don't see duplicates.
Your code returns the following on the data you provided:
Id ParentId
1
2 1
3 1
5 3
4 2
which is what you want.
Here is a db<>fiddle.
Here is the code:
WITH t as (
SELECT *
FROM (VALUES (1, NULL), (2, 1), (3, 1), (4, 2), (5, 3), (6, 8), (7, 9)
) v(id, parentId)
),
CTE AS (
SELECT Id, ParentId
FROM t
WHERE ParentId IS NULL
UNION ALL
SELECT t.Id, t.ParentId
FROM t
INNER JOIN CTE c ON t.ParentId = c.Id
)
SELECT *
FROM CTE;
If you are getting duplicates in your actual result set, then you presumably have duplicates in your original table. I would recommend removing them before doing the recursive logic:
with t as (
select distinct id, parentid
from <your query>
),
. . .
Then run the recursive logic.
Try this sql script which result Parent Child Hierarchy
;WITH CTE(Id , ParentId)
AS
(
SELECT 1 , NULL UNION ALL
SELECT 2 , 1 UNION ALL
SELECT 3 , 1 UNION ALL
SELECT 4 , 2 UNION ALL
SELECT 5 , 3 UNION ALL
SELECT 6 , 8 UNION ALL
SELECT 7 , 9
)
,Cte2
AS
(
SELECT Id ,
ParentId ,
CAST('\'+ CAST(Id AS VARCHAR(MAX))AS VARCHAR(MAX)) AS [Hierarchy]
FROM CTE
WHERE ParentId IS NULL
UNION ALL
SELECT c1.Id ,
c1.ParentId ,
[Hierarchy]+'\'+ CAST(c1.Id AS VARCHAR(MAX)) AS [Hierarchy]
FROM Cte2 c2
INNER JOIN CTE c1
ON c1.ParentId = c2.Id
)
SELECT Id,
RIGHT([Hierarchy],LEN([Hierarchy])-1) AS ParentChildHierarchy
FROM Cte2
GO
Result
Id ParentChildHierarchy
-------------------------
1 1
2 1\2
3 1\3
5 1\3\5
4 1\2\4
This query will help you
CREATE TABLE #table( ID INT, ParentId INT )
INSERT INTO #table(ID,ParentId)
VALUES (1, NULL ), (2, 1 ), (3, 1 ), (4, 2 ), (5, 3 ), (6, 8 ), (7, 9 )
;WITH CTE AS (
SELECT ID FROM #table WHERE PARENTID IS NULL
UNION ALL
SELECT T.ID FROM #table T
INNER JOIN #table T1 ON T.PARENTID =T1.ID
) SELECT * FROM CTE
Original
RecordKey Name Section1_Product Section1_Code Section2_Product Section2_Code ......
1 a ff 22
2 b gg 22
3 c hh 33
RecordKey Name Section Product Code ......
1 a 1 ff 22
1 a 2
2 b 1 gg 22
2 b 2
3 c 1 hh 22
3 c 2
I am trying to unpivot the columns into rows. Some sections will have null value.
SELECT RecordKey
,Name
,'Num_of_Sections' = ROW_NUMBER() OVER (PARTITION BY RecordKey ORDER BY ID)
,Product
,Code
FROM (
SELECT RecordKey, Name, Section1_Product, Section1_Code, Section2_Product, Section2_Code FROM Table
) M
UNPITVOT (
Product FOR ID IN (Section1_Product, Section2_Product)
) p
UNPIVOT (
Code FOR CO IN (Section1_Code, Section2_Code)
) c
If I execute with only one column (Product, comment out Code) then I will have 2 values in ID column (1,2). If I run the query with 2 columns then I get 4 values in ID column(1, 2, 3, 4).
may as per my assumption and your data provided we can achieve this using Cross apply and Row_number
declare #Record TABLE
([RecordKey] int,
[Name] varchar(1),
[Section1_Product] varchar(2),
[Section1_Code] int,
[Section2_Product] varchar(2),
[Section2_Code] int)
;
INSERT INTO #Record
([RecordKey], [Name], [Section1_Product], [Section1_Code],[Section2_Product],[Section2_Code])
VALUES
(1, 'a', 'ff', 22,NULL,NULL),
(2, 'b', 'gg', 22,NULL,NULL),
(3, 'c', 'hh', 33,NULL,NULL)
;
With cte as (
Select T.RecordKey,
T.Name,
T.val,
T.val1 from (
select RecordKey,Name,val,val1 from #Record
CROSS APPLY (VALUES
('Section1_Product',Section1_Product),
('Section2_Product',Section2_Product))cs(col,val)
CROSS APPLY (VALUES
('Section1_Code',Section1_Code),
('Section2_Code',Section2_Code))css(col1,val1)
WHERE val is NOT NULL)T
)
Select c.RecordKey,
c.Name,
c.RN,
CASE WHEN RN = 2 THEN NULL ELSE c.val END Product,
c.val1 Code
from (
Select RecordKey,
Name,
ROW_NUMBER()OVER(PARTITION BY val ORDER BY (SELECT NULL))RN,
val,
val1 from cte )C
This is kind of hard to explain in words but here is an example of what I am trying to do in SQL. I have a query which returns the following records:
ID Z
--- ---
1 A
1 <null>
2 B
2 E
3 D
4 <null>
4 F
5 <null>
I need to filter this query so that each unique record (based on ID) appears only once in the output and if there are multiple records for the same ID, the output should contain the record with the value of Z column being non-null. If there is only a single record for a given ID and it has value of null for column Z the output still should return that record. So the output from the above query should look like this:
ID Z
--- ---
1 A
2 B
2 E
3 D
4 F
5 <null>
How would you do this in SQL?
You can use GROUP BY for that:
SELECT
ID, MAX(Z) -- Could be MIN(Z)
FROM MyTable
GROUP BY ID
Aggregate functions ignore NULLs, returning them only when all values on the group are NULL.
If you need to return both 2-B and 2-E rows:
SELECT *
FROM YourTable t1
WHERE Z IS NOT NULL
OR NOT EXISTS
(SELECT * FROM YourTable t2
WHERE T2.ID = T1.id AND T2.z IS NOT NULL)
SELECT ID
,Z
FROM YourTable
WHERE Z IS NOT NULL
DECLARE #T TABLE ( ID INT, Z CHAR(1) )
INSERT INTO #T
( ID, Z )
VALUES ( 1, 'A' ),
( 1, NULL )
, ( 2, 'B' ) ,
( 2, 'E' ),
( 3, 'D' ) ,
( 4, NULL ),
( 4, 'F' ),
( 5, NULL )
SELECT *
FROM #T
; WITH c AS (SELECT ID, r=COUNT(*) FROM #T GROUP BY ID)
SELECT t.ID, Z
FROM #T t JOIN c ON t.ID = c.ID
WHERE c.r =1
UNION ALL
SELECT t.ID, Z
FROM #T t JOIN c ON t.ID = c.ID
WHERE c.r >=2
AND z IS NOT NULL
This example assumes you want two rows returned for ID = 2.
with tmp (id, cnt_val) as
(select id,
sum(case when z is not null then 1 else 0 end)
from t
group by id)
select t.id, t.z
from t
inner join tmp on t.id = tmp.id
where tmp.cnt_val > 0 and t.z is not null
or tmp.cnt_val = 0 and t.z is null
WITH CTE
AS (
SELECT id
,z
,ROW_NUMBER() OVER (
PARTITION BY id ORDER BY coalesce(z, '') DESC
) rn
FROM #T
)
SELECT id
,z
FROM CTE
WHERE rn = 1
I have a table in SqlServer 2008 with data of the form
UserID StartWeek EndWeek Type
1 1 3 A
1 4 5 A
1 6 10 A
1 11 13 B
1 14 16 A
2 1 5 A
2 6 9 A
2 10 16 B
I'd like to consolidate/condense the adjacent types so that the resulting table looks like this.
UserID StartWeek EndWeek Type
1 1 10 A
1 11 13 B
1 14 16 A
2 1 9 A
2 10 16 B
Does anyone have any suggestions as to the best way to accomplish this? I've been looking at using Row_number and Partition, but I can't get it to behave exactly as I'd like.
There's probably a neater way to do it, but this produces the correct result
DECLARE #t TABLE
(UserId TINYINT
,StartWeek TINYINT
,EndWeek TINYINT
,TYPE CHAR(1)
)
INSERT #t
SELECT 1,1,3,'A'
UNION SELECT 1,4,5,'A'
UNION SELECT 1,6,10,'A'
UNION SELECT 1,11,13,'B'
UNION SELECT 1,14,16,'A'
UNION SELECT 2,1,5,'A'
UNION SELECT 2,6,9,'A'
UNION SELECT 2,10,16,'B'
;WITH srcCTE
AS
(
SELECT *
,ROW_NUMBER() OVER (PARTITION BY t1.UserID, t1.Type
ORDER BY t1.EndWeek
) AS rn
FROM #t AS t1
)
,recCTE
AS
(
SELECT *
,0 AS grp
FROM srcCTE
WHERE rn = 1
UNION ALL
SELECT s.UserId
,s.StartWeek
,s.EndWeek
,s.TYPE
,s.rn
,CASE WHEN s.StartWeek - 1 = r.EndWeek
THEN r.grp
ELSE r.grp+ 1
END AS GRP
FROM srcCTE AS s
JOIN recCTE AS r
ON r.UserId = s.UserId
AND r.TYPE = s.TYPE
AND r.rn = s.rn - 1
)
SELECT UserId
,MIN(StartWeek) AS StartWeek
,MAX(EndWeek) AS EndWeek
,TYPE
FROM recCTE AS s1
GROUP BY UserId
,TYPE
,grp
Also using a CTE, but in a slightly different way
DECLARE #Consolidate TABLE (
UserID INTEGER, StartWeek INTEGER,
EndWeek INTEGER, Type CHAR(1))
INSERT INTO #Consolidate VALUES (1, 1, 3, 'A')
INSERT INTO #Consolidate VALUES (1, 4, 5, 'A')
INSERT INTO #Consolidate VALUES (1, 6, 10, 'A')
INSERT INTO #Consolidate VALUES (1, 14, 16, 'A')
INSERT INTO #Consolidate VALUES (1, 11, 13, 'B')
INSERT INTO #Consolidate VALUES (2, 1, 5, 'A')
INSERT INTO #Consolidate VALUES (2, 6, 9, 'A')
INSERT INTO #Consolidate VALUES (2, 10, 16, 'B')
;WITH ConsolidateCTE AS
(
SELECT UserID, StartWeek, EndWeek, Type
FROM #Consolidate
UNION ALL
SELECT cte.UserID, cte.StartWeek, c.EndWeek, c.Type
FROM ConsolidateCTE cte
INNER JOIN #Consolidate c ON
c.UserID = cte.UserID
AND c.StartWeek = cte.EndWeek + 1
AND c.Type = cte.Type
)
SELECT UserID, [StartWeek] = MIN(Startweek), EndWeek, Type
FROM (
SELECT UserID, Startweek, [EndWeek] = MAX(EndWeek), Type
FROM ConsolidateCTE
GROUP BY UserID, StartWeek, Type
) c
GROUP BY UserID, EndWeek, Type
ORDER BY 1, 2, 3