Below is the case, this is simple group data but join / grouping picking duplicate data....
CREATE TABLE #t1 (UnID bigint IDENTITY(1,1), name varchar(5), id bigint)
insert into #t1 (name,id)values('a',1)
insert into #t1 (name,id)values('a',2)
insert into #t1 (name,id)values('a',3)
insert into #t1 (name,id)values('a',4)
insert into #t1 (name,id)values('b',5)
insert into #t1 (name,id)values('c',6)
CREATE TABLE #t2 (name varchar(5), id bigint)
insert into #t2 (name,id)values('a',1)
insert into #t2 (name,id)values('a',2)
insert into #t2 (name,id)values('a',3)
insert into #t2 (name,id)values('b',4)
insert into #t2 (name,id)values('c',5)
select
a.UnID, a.name as aName, a.Id as aId, b.Id as bId
into #t3
from #t1 a
join #t2 b on a.name = b.name
select max(bid),unid from #t3
group by UnId
Grouping Result
bid unid
3 1
3 2
3 3
3 4
4 5
5 6
Grouping desired Result
bid unid
1 1
2 2
3 3
NULL 4
4 5
5 6
and if bid not found then null
Below make me possible BUT still looking for better approach, xid is extra field for holding loop value....
CREATE TABLE #t4 (ID bigint IDENTITY(1,1), bid bigint)
SELECT
a.UnID, a.name as aName, a.Id as aId, b.Id as bId,NULL as xid
INTO #t3
FROM #t1 a
JOIN #t2 b ONa.name = b.name
DECLARE t_cursor CURSOR FOR
SELECT max(bid),unid
FROM #t3
GROUP BY UnId
DECLARE #bid bigint, #unid bigint
OPEN t_cursor
FETCH NEXT FROM t_cursor
INTO #bid, #unid
WHILE ##FETCH_STATUS = 0
BEGIN
DECLARE #maxBid BIGINT
SET #maxBid = 0
SELECT #maxBid = max(bid)
FROM #t3
WHERE UnId= #unid
AND NOT bid IN (SELECT #t4.bid FROM #t4) GROUP BY UnId
INSERT into #t4
SELECT #maxBid as bid
set #maxBid = isnull(#maxBid,0)
if(#maxBid != 0)
begin
update #t3 set xid = #maxBid where unid = #unid
end
FETCH NEXT FROM t_cursor
INTO #bid, #unid
END
CLOSE t_cursor;
DEALLOCATE t_cursor;
SELECT * FROM #t3 order by unid,bid
SELECT max(xid),unid from #t3
group by UnId
Related
as a follow up on my question original question posted here
UPDATE in Batches Does Not End and Remaining Data Does Not Get Updated
If you use the logic below you'll see that update never finishes. Let me know if you have any ideas why...
Table 1
IF OBJECT_ID('tempdb..#Table2') IS NOT NULL
BEGIN
DROP TABLE #Table2;
END
CREATE TABLE #Table2 (ID INT);
DECLARE #Count int = 0;
WHILE (select count(*) from #Table2) < 10000 BEGIN
INSERT INTO #Table2 (ID)
VALUES (#Count)
-- Make sure we have a unique id for the test, else we can't identify 10 records
set #Count = #Count + 1;
END
Table 2
IF OBJECT_ID('tempdb..#Table1') IS NOT NULL
BEGIN
DROP TABLE #Table1;
END
CREATE TABLE #Table1 (ID INT);
DECLARE #Count int = 0;
WHILE (select count(*) from #Table1) < 5000 BEGIN
INSERT INTO #Table1 (ID)
VALUES (#Count)
-- Make sure we have a unique id for the test, else we can't identify 10 records
set #Count = #Count + 1;
END
/****************** UPDATE ********************/
select count (*) from #Table2 t2 where Exists (select * from #Table1 t1 where t1.ID = t2.ID)
select count (*) from #Table2 where ID = 0
select count (*) from #Table1 where ID = 0
-- While exists an 'un-updated' record continue
WHILE exists (select 1 from #Table2 t2 where Exists (select * from #Table1 t1 where t1.ID = t2.ID) )
BEGIN
-- Update any top 10 'un-updated' records
UPDATE t2
SET ID = 0
FROM #Table2 t2
WHERE ID IN (select top 10 id from #Table2 where Exists (select * from #Table1 t1 where t1.ID = t2.ID) )
END
Your UPDATE statement is referencing the wrong instance on #Table2. You want the following:
UPDATE t2 SET
ID = 0
FROM #Table2 t2
WHERE ID IN (
SELECT TOP 10 ID
-- note this alias is t2a, and is what the `exists` needs to reference
-- not the table being updated (`t2`)
FROM #Table2 t2a
WHERE EXISTS (SELECT 1 FROM #Table1 t1 WHERE t1.ID = t2a.ID)
)
Note: For testing ensure that #Count starts from 1 not 0 else you do still end up with an infinite loop.
I have Parent and Child table.
The goal is to duplicate the records, except with new primary keys.
Original Tables
Parent(id)
1
Child(id,parentId, data)
1,1
2,1
After insert:
Parent
1
2
Child
1,1
2,1
3,2
4,2
How do I do that? The part I am having trouble with is getting the new parent key for use with the child records.
This is what I have come up with so far.
--DECLARE VARS
declare #currentMetadataDocumentSetId int = 1, --Ohio
#newMetadataDocumentSetid int = 3; --PA
--CLEANUP
IF OBJECT_ID('tempdb..#tempFileRowMap') IS NOT NULL
/*Then it exists*/
DROP TABLE #tempFileRowMap
--Remove existing file row maps.
delete from file_row_map where metadata_document_set_id = #newMetadataDocumentSetid;
--Create a temptable to hold data to be copied.
Select [edi_document_code],
[functional_group],
[description],
3 as [metadata_document_set_id],
[document_name],
[incoming_file_row_subtype],
[metadata_document_id],
[document_subcode],
[outgoing_file_row_subtype],
[asi_type_code],
[asi_action_code],
[metadata_document_set],
file_row_map_id as orig_file_row_map_id
into #tempFileRowMap
from file_row_map fileRowMap
where metadata_document_set_id = #currentMetadataDocumentSetId;
--Select * from #tempFileRowMap;
Insert into file_row_map select
[edi_document_code],
[functional_group],
[description],
[metadata_document_set_id],
[document_name],
[incoming_file_row_subtype],
[metadata_document_id],
[document_subcode],
[outgoing_file_row_subtype],
[asi_type_code],
[asi_action_code],
[metadata_document_set]
from #tempFileRowMap
--Show Results
Select * from file_row_map fileRowMap where fileRowMap.metadata_document_set_id = #newMetadataDocumentSetid
--Update Detail
Select
[file_row_map_id],
[file_row_column],
[element_code],
[element_metadata_id],
[col_description],
[example],
[translate],
[is_used],
[is_mapped],
[page_num],
[subcode],
[qualifier],
[loop_code],
[loop_subcode],
[default_value],
[delete_flag]
into #tempFileRowMapDetail
from [dbo].[file_row_map_detail] d
left join #tempFileRowMap m
on m.orig_file_row_map_id = d.file_row_map_id
select * from #tempFileRowMapDetail
Simply use OUTPUT clause for getting exact Parent Table Primary Key values.
Lets build Example Schema for your case
--For Capturing inserted ID
CREATE TABLE #ID_CAPTURE (PARENT_ID INT,ORDER_NME VARCHAR(20));
--Your Intermidiate Data To insert into Actual Tables
CREATE TABLE #DUMMY_TABLE (ORDER_NME VARCHAR(20), ITEM_NME VARCHAR(20));
--Actual Tables
CREATE TABLE #ORDER_PARENT (ORDER_ID INT IDENTITY,ORDER_NME VARCHAR(20))
CREATE TABLE #ORDER_CHILD (CHILD_ID INT IDENTITY ,ORDER_ID INT, ORDER_NME VARCHAR(20))
INSERT INTO #DUMMY_TABLE
SELECT 'BILL1','Oil'
UNION ALL
SELECT 'BILL1', 'Gas'
UNION ALL
SELECT 'BILL2', 'Diesel'
Now do Inserts in Parent & Child Tables
INSERT INTO #ORDER_PARENT
OUTPUT inserted.ORDER_ID, inserted.ORDER_NME into #ID_CAPTURE
SELECT DISTINCT ORDER_NME FROM #DUMMY_TABLE
INSERT INTO #ORDER_CHILD
SELECT C.PARENT_ID, ITEM_NME FROM #DUMMY_TABLE D
INNER JOIN #ID_CAPTURE C ON D.ORDER_NME = C.ORDER_NME
SELECT * FROM #ID_CAPTURE
SELECT * FROM #ORDER_CHILD
There are other ways to get Inserted Identity values.
See documentation ##IDENTITY (Transact-SQL) , SCOPE_IDENTITY
Try following approach:
DECLARE #Table1 TABLE (
ID INT NOT NULL PRIMARY KEY,
ParentID INT NULL, -- FK
[Desc] VARCHAR(50) NOT NULL
);
INSERT #Table1 (ID, ParentID, [Desc])
VALUES
(1, NULL, 'A'),
(2, 1, 'AA.1'),
(3, 1, 'AA.2'),
(4, NULL, 'B'),
(5, 4, 'BB.1'),
(6, 4, 'BB.2'),
(7, 4, 'BB.3'),
(8, 7, 'BBB.1');
DECLARE #ParentID INT = 4;
DECLARE #LastID INT = (SELECT TOP(1) ID FROM #Table1 x ORDER BY x.ID DESC)
IF #LastID IS NULL
BEGIN
RAISERROR('Invalid call', 16, 1)
--RETURN ?
END
SELECT #LastID AS LastID;
/*
LastID
-----------
8
*/
DECLARE #RemapIDs TABLE (
OldID INT NOT NULL PRIMARY KEY,
[NewID] INT NOT NULL UNIQUE
);
WITH CteRecursion
AS (
SELECT 1 AS Lvl, crt.ID, crt.ParentID --, crt.[Desc]
FROM #Table1 crt
WHERE crt.ID = #ParentID
UNION ALL
SELECT cld.Lvl + 1 AS Lvl, crt.ID, crt.ParentID --, crt.[Desc]
FROM #Table1 crt
JOIN CteRecursion cld ON crt.ParentID = cld.ID
)
INSERT #RemapIDs (OldID, [NewID])
SELECT r.ID, #LastID + ROW_NUMBER() OVER(ORDER BY r.Lvl) AS [NewID]
FROM CteRecursion r;
--INSERT #Table1 (ID, ParentID, [Desc])
SELECT nc.[NewID] AS ID, np.[NewID] AS ParentID, o.[Desc]
FROM #Table1 o -- old
JOIN #RemapIDs nc /*new child ID*/ ON o.ID = nc.OldID
LEFT JOIN #RemapIDs np /*new parent ID*/ ON o.ParentID = np.OldID
/*
ID ParentID Desc
----------- ----------- --------------------------------------------------
9 NULL B
10 9 BB.1
11 9 BB.2
12 9 BB.3
13 12 BBB.1
*/
Note: with some minor changes should work w. many ParentIDs values.
I am working with SQL Server Triggers. And I need a sql query to find columns from a table whose values has been updated using of INSERTED and DELETED tables.
Can anyone help me out on this ? For ex -
DECLARE #T1 TABLE (Name NVARCHAR(MAX), LName nvarchar(max), Address1 Nvarchar(max),id int)
DECLARE #T2 TABLE (Name NVARCHAR(MAX), LName nvarchar(max), Address1 Nvarchar(max), id int)
insert into #T1 values('Ricky','Broad','a b road',1)
insert into #T1 values('Mike','Halls','m g road',2)
insert into #T2 values('Ricky_Update','Broad','a b road',1)
insert into #T2 values('Mike','Halls','m g road',2)
;WITH ChangedData AS (
SELECT d.name , d.LName FROM #T1 d
EXCEPT
SELECT i.name , i.LName FROM #T2 i
)
I tried to find out by "EXCEPT" but it's returning whole updated row. And I need only updated columns like in above example I only need-
Name column for id =1 because it's updated.
You have to do except for individual columns and then do Union.
SELECT NAME
FROM (
SELECT d.NAME
FROM #T1 d
WHERE id = 1
EXCEPT
SELECT i.NAME
FROM #T2 i
WHERE id = 1
) A
UNION
SELECT LName
FROM (
SELECT d.LName
FROM #T1 d
WHERE id = 1
EXCEPT
SELECT i.LName
FROM #T2 i
WHERE id = 1
) B
SQL Server 2008
I have a table with 2 fields among others
TableA
alterid uniqueidentifier
revnum varchar(50)
Another table has the exact fields as the first one
TableB
alterid uniqueidentifier
revnum varchar(50)
I want to check compare the revnum fields. For each record of TableA I check if there is a record in TableB. If it exists and the revnum fields are different I update the TableB.revnum to the TebleA.revnum. If it does not exist then I add the record from TableA to TableB.
Here is the code
BEGIN
SET #Prod_curs =CURSOR FOR SELECT s.alterid, s.revnum, r.revnum FROM TableA s LEFT OUTER JOIN
TableB r ON s.alterid=r.alterid
WHERE s.revnum<>r.revnum OR r.revnum IS NULL
OPEN #Prod_curs
FETCH NEXT FROM #Prod_curs INTO #alterid, #srevnum, #rrevnum
WHILE ##FETCH_STATUS=0
BEGIN
IF #rrevnum IS NULL
BEGIN
INSERT INTO TableB (alterid,revnum) VALUES (#alterid,ISNULL(#srevnum,0))
END
IF #srevnum<>#rrevnum
BEGIN
UPDATE TableB SET revnum=#srevnum WHERE alterid=#alterid
END
FETCH NEXT FROM #Prod_curs INTO #alterid, #srevnum, #rrevnum
END
CLOSE #Prod_curs;
DEALLOCATE #Prod_curs;
END
My code works fine in a set of about 15000 records except some records where a weird thing happens.
In TableA I have this record
alterid revnum
'A770B280-B4DA-4937-9046-B24E60259AB6' '2-414922-1--1-1-2-51-0'
When it is store in TableB the values are
alterid revnum
'BF18A0EB-A684-486B-B053-55BC2969F1E3' '2-414922-1--1-1-2-51-0'
For some reason the alterid changes and I can't figure out why.
Can someone help
seems like code is working fine its just select that you are doing after insert/update.
i do have some unclear thing in your post as well.
Does the column "revnum" is primary key or has unique constraint on it in TableB ?
the result you have shown what is the select stmt you use for TableB ?
was it something like "Select top 1 * from TABLEB where revnum=xyz" ?
i ran you logic on dummy data and update/insert is fine.
check this code. what i doubt is in TABLEB you already have some "AlterID" with same "rvnum" value that is not present in "TableA"
see the below result where instead (3,3) i am also seeing (4,3)
SET NOCOUNT ON
DECLARE #t1 TABLE
(
id INT
,VALUE INT
)
INSERT INTO #t1 ( id, VALUE )
SELECT 1,1 UNION ALL SELECT 2,2 UNION ALL SELECT 3,3
DECLARE #t2 TABLE
(
id INT
,VALUE INT
)
INSERT INTO #t2 ( id, VALUE )
SELECT 1,1 UNION ALL SELECT 2,2 UNION ALL SELECT 4,3
DECLARE #Value1 INT,#Value2 INT,#ID INT ,#Prod_curs CURSOR
SET #Prod_curs =CURSOR FOR
SELECT s.id, s.Value, r.value
FROM #t1 s LEFT OUTER JOIN
#t2 r ON s.id=r.ID
WHERE s.Value<>r.Value OR r.Value IS NULL
OPEN #Prod_curs
FETCH NEXT FROM #Prod_curs INTO #ID, #Value1, #Value2
WHILE ##FETCH_STATUS=0
BEGIN
IF #Value2 IS NULL
BEGIN
INSERT INTO #T2 (ID,Value) VALUES (#ID,ISNULL(#Value1,0))
END
IF #Value1<>#Value2
BEGIN
UPDATE #T2 SET Value=#Value2 WHERE ID=#ID
END
FETCH NEXT FROM #Prod_curs INTO #ID, #Value1, #Value2
END
CLOSE #Prod_curs;
DEALLOCATE #Prod_curs;
SELECT TOP 1 * FROM #t2 WHERE VALUE = 3
I need a SQL statement which fills the null values from the second column of #T1 table with values from #T2(C1).
There is no foreign key or match between the columns of those two tables.
Sample:
T1 (C1, T2C1)
A1, 1
A2, null
A3, null
A4, 4
A5, null
-------------
T2 (C1)
a
b
After update, the T1 will look like:
A1, 1
A2, a
A3, b
A4, 4
A5, null
I found two approaches:
Using CTE
create table #T1 (C1 varchar(10), T2C1 varchar(10))
create table #T2 (C1 varchar(10))
insert into #T1 values ('A1', '1')
insert into #T1 values ('A2', null)
insert into #T1 values ('A3', null)
insert into #T1 values ('A4', '4')
insert into #T1 values ('A5', null)
insert into #T2 values ('a')
insert into #T2 values ('b')
;with t2 as
(
select C1, row_number() over (order by C1) as Index2
from #T2
)
,t1 as
(
select T2C1, row_number() over (order by C1) as Index1
from #T1
where T2C1 is null
)
update t1
set t1.T2C1 = t2.C1
from t2
where t1.Index1 = t2.Index2
select * from #T1
drop table #T1
drop table #T2
With Derived Tables
create table #T1 (C1 varchar(10), T2C1 varchar(10))
create table #T2 (C1 varchar(10))
insert into #T1 values ('A1', '1')
insert into #T1 values ('A2', null)
insert into #T1 values ('A3', null)
insert into #T1 values ('A4', '4')
insert into #T1 values ('A5', null)
insert into #T2 values ('a')
insert into #T2 values ('b')
update #T1
set T2C1 = cj.C1
from #T1
join (select T2C1, row_number() over (order by C1) as Index1, C1
from #T1
where T2C1 is null) ci on ci.C1 = #T1.C1
join (select C1, row_number() over (order by C1) as Index2
from #T2) cj on ci.Index1 = cj.Index2
select * from #T1
drop table #T1
drop table #T2
My question is, can I achieve this without using windowing functions and with no cursors?
Update
#Damien_The_Unbeliever correctly points that to do this kind of update it is not possible without defining an ordering on tables, actually I think exactly said is without properly identify and link the rows from target table.
#Bogdan Sahlean has found another way, using table variables and IDENTITY column, which I'm happy with this solution, it's another way
However, in the real application I will still use the windowing functions
Thanks all
1.I suppose you have a pk in target table (#T1).
2.Instead of ROW_NUMBER this solution uses IDENTITY(1,1) columns and two table variables.
3.I didn't tested this solution.
DECLARE #t2_count INT = (SELECT COUNT(*) FROM #T2);
DECLARE #Target TABLE
(
MyId INT IDENTITY(1,1) PRIMARY KEY
,T1_pk INT NOT NULL UNIQUE
);
INSERT #Target (T1_pk)
SELECT TOP(#t2_count) pk
FROM #T1
WHERE T2C1 IS NULL;
DECLARE #Source TABLE
(
MyId INT IDENTITY(1,1) PRIMARY KEY
,C1 VARCHAR(10) NOT NULL
);
INSERT #Source (C1)
SELECT C1
FROM #T2;
UPDATE #T1
SET T2C1 = src.C1
FROM #T1 t
INNER JOIN #Target trg ON t.pk = trg.T1_pk
INNER JOIN #Source src ON trg.MyId = src.MyId;
I agree with Damien, anyway when you will rely on the engine and make a premise that the table is ordered by the C1 column (which depends only on the DB and you can not rely on that ) the you could issue an update statement which will update all the rows
declare #a int
set #a = 0
update #t1
set t2c1 = #a,
#a = #a+1
But I wouldn't do that.
I would use a CTE along with ROW_NUMBER:
WITH cte_t1 AS
(
SELECT
T2C1
,ROW_NUMBER() OVER (ORDER BY C1) AS id
FROM T1
WHERE T2C1 IS NULL
)
,cte_t2 AS
(
SELECT
C1
,ROW_NUMBER() OVER (ORDER BY C1) AS id
FROM T2
)
UPDATE t1
SET t1.T2C1 = t2.C1
FROM cte_t1 AS t1
INNER JOIN cte_t2 AS t2
ON t1.id = t2.id
;
The ROW_NUMBER creates an identifier based on the order the columns are in (which I am assuming is what you are looking for, but note that SQL does not have an order instead this query is relying on the physical order of the records which is not a good idea but it looks like that is what you are dealing with). We then join on this identifier and update the records in T1 with the values in T2.
I have placed this solution on SQL Fiddle.
If you can look into create new tables with some type of identifier that links them together, then you would not have to rely on the physical order of the records.