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'm trying to compare null values using not in and exists but I found some diferences
Why the latest query returns null? is it related to a db parameter?
--SET ANSI_NULLS On
CREATE TABLE #Tmp (id INT)
CREATE TABLE #Tmp1 (id INT)
INSERT INTO #Tmp(id) VALUES (1)
INSERT INTO #Tmp(id) VALUES (null)
INSERT INTO #Tmp1(id) VALUES (1)
INSERT INTO #Tmp1(id) VALUES (null)
SELECT id FROM #Tmp WHERE id IN (SELECT id FROM #tmp1)
SELECT id FROM #Tmp WHERE EXISTS(SELECT 1 FROM #Tmp1 WHERE #Tmp1.id = #Tmp.id)
SELECT id FROM #Tmp WHERE id NOT IN (SELECT id FROM #tmp1)
SELECT id FROM #Tmp WHERE NOT EXISTS(SELECT 1 FROM #Tmp1 WHERE #Tmp1.id = #Tmp.id)
DROP TABLE #Tmp
DROP TABLE #Tmp1
In SQL Server, remember that NULL does not return an equality check of TRUE with anything (unless you are playing with ANSI_NULLS):
SELECT 1
WHERE 1=NULL
SELECT 1
WHERE NULL = NULL
It only returns True when you use IS/IS NOT
SELECT 1
WHERE NULL IS NULL
For your last query, the only record that satisfies that requirement in the WHERE clause is your NULL row. The equivalent is:
SELECT id FROM #Tmp
WHERE id IS NULL
To solve your issue, throw a second clause on there:
SELECT id FROM #Tmp
WHERE NOT EXISTS(
SELECT 1
FROM #Tmp1
WHERE #Tmp1.id = #Tmp.id OR #Tmp.id IS NULL
)
It is very simple:
id IN (1, NULL)
<=>
id = 1 OR id = NULL
And negation of alternative:
id NOT IN (1, NULL)
<=>
id != 1 AND id != NULL -- yields NULL
Summary:
If you want to use NOT IN make sure your list does not contain NULL.
SELECT id FROM #Tmp WHERE id NOT IN (SELECT id FROM #tmp1 WHERE id IS NOT NULL)
SELECT id FROM #Tmp WHERE id NOT IN (SELECT COALESCE(id,-1) FROM #tmp1)
I'm using SQL Server 2008.
I have an after trigger for INSERT, UPDATE and DELETE action defined in the table. My problem is that currently my trigger inserts one record at a time and I need multiple records as for one
SELECT TOP 1 #ParentID FROM ... WHERE ID = #ID
returns multiple unique records.
(See this comment below "-- this subquery returns more than 1 value, so I need to insert in the search Audit table as many ParentIDs as it returns")
I believe I need to use cursor, but I'm not sure where exactly to declare and open cursor.
--CREATE PROCEDURE [dbo].[SP_Auditing]
-- #ID INT, #Code VARCHAR(3), #AuditType VARCHAR(10), #ParentCode VARCHAR(3) = NULL, #ParentID INT = NULL
--AS
--BEGIN
-- INSERT INTO myDB.dbo.Table1 (ID, Code, AuditType, ParentCode, ParentID)
-- VALUES(#ID, #Code, #AuditType, #ParentCode, #ParentID)
--END
GO
CREATE TRIGGER [dbo].[Tr_MyFavouriteTable_UPD_INSERT_DEL] ON [dbo].[MyFavouriteTable] AFTER INSERT, DELETE, UPDATE NOT FOR REPLICATION
AS
BEGIN
DECLARE #ID INT, #Code VARCHAR(3), #AuditType VARCHAR(10), #ParentCode VARCHAR(3), #ParentID INT SET #Code = 'DOC'
IF EXISTS (SELECT 1 FROM inserted) AND
NOT EXISTS (SELECT 1 FROM deleted)
BEGIN
SELECT TOP 1
#ID = ins.ID,
#ParentID = (
SELECT TOP 1 CAST(RIGHT(parentId,LEN(parentId) - LEN(LEFT(parentId,3))) AS INT)
FROM [MyDB].[dbo].[MyFavouriteTable] t WITH (NOLOCK)
INNER JOIN [MyDB2].[dbo].[MyView] v WITH (NOLOCK)
ON t.Id = v.ID
WHERE v.ID = #ID --284
), **-- this subquery returns more than 1 value, so I need to insert in the search Audit table as many ParentIDs as it returns**
#AuditType = 'INSERT' FROM inserted ins
IF #ID IS NOT NULL
AND
#ParentID IS NOT NULL
AND
#ParentCode IS NOT NULL
EXEC [MyDB].[dbo].SP_Auditing] #ID, #Code, #AuditType, #ParentCode, #ParentID
END
-- below is the same logic for UPDATE and DELETE actions...
The stored procedure above simply inserts data into the Audit table.
Never use scalar variables in triggers because insert, update, and delete may affect multiple rows. As to your trigger, try something like this.
CREATE TRIGGER [dbo].[Tr_MyFavouriteTable_UPD_INSERT_DEL]
ON [dbo].[MyFavouriteTable] AFTER INSERT, DELETE, UPDATE
NOT FOR REPLICATION
AS
BEGIN
;with act as (
select isnull(i.id,d.id) id, --either deleted or inserted is not null
case when i.id is not null and d.id is not null then 'update'
when i.id is not null then 'insert'
else 'delete' end auditType
from inserted i full outer join deleted d on i.id = d.id
),
audit_cte as (
SELECT act.id, 'DOC' Code,
CAST(RIGHT(parentId,LEN(parentId) - LEN(LEFT(parentId,3))) AS INT) parentid,
act.auditType, 'parentcode' parentCode
FROM [MyDB].[dbo].[MyFavouriteTable] t WITH (NOLOCK)
INNER JOIN [MyDB2].[dbo].[MyView] v WITH (NOLOCK) ON t.Id = v.ID
inner join act on act.id = t.id
)
insert myDB.dbo.Table1 (ID, Code, AuditType, ParentCode, ParentID)
select id,code,AuditType, ParentCode, ParentID
from audit_cte
where parentCode is not null and parentid is not null
end
Why do you need to get the records one by one? From my understanding you want to keep the log.
IF EXISTS (SELECT 1 FROM inserted) AND
NOT EXISTS (SELECT 1 FROM deleted)
BEGIN
INSERT INTO [Your_Log_Table]
SELECT
ins.ID, [Code],'INSERT',[PrentCode],
(SELECT TOP 1 CAST(RIGHT(parentId,LEN(parentId) -
LEN(LEFT(parentId,3))) AS INT)
FROM [MyDB].[dbo].[MyFavouriteTable] t WITH (NOLOCK)
INNER JOIN [MyDB2].[dbo].[MyView] v WITH (NOLOCK)
ON t.Id = v.ID
WHERE v.ID = ins.ID --284
)
FROM inserted ins
END
See Alex Kudryashev's answer. I needed to tweak a little his logic to sort out duplicate records with the same ParentIDs for the insertion into the Audit table. I added one more cte just below Alex's cte_Audit as follows
CREATE TRIGGER [dbo].[Tr_MyFavouriteTable_UPD_INSERT_DEL]
ON [dbo].[MyFavouriteTable] AFTER INSERT, DELETE, UPDATE
NOT FOR REPLICATION
AS
BEGIN
;with act as (
select isnull(i.id,d.id) id, --either deleted or inserted is not null
case when i.id is not null and d.id is not null then 'update'
when i.id is not null then 'insert'
else 'delete' end auditType
from inserted i full outer join deleted d on i.id = d.id
),
audit_cte as (
SELECT act.id, 'DOC' Code,
CAST(RIGHT(parentId,LEN(parentId) - LEN(LEFT(parentId,3))) AS INT) parentid,
act.auditType, 'parentcode' parentCode
FROM [MyDB].[dbo].[MyFavouriteTable] t WITH (NOLOCK)
INNER JOIN [MyDB2].[dbo].[MyView] v WITH (NOLOCK) ON t.Id = v.ID
inner join act on act.id = t.id
)
insert myDB.dbo.Table1 (ID, Code, AuditType, ParentCode, ParentID)
select id,code,AuditType, ParentCode, ParentID
from audit_cte
where parentCode is not null and parentid is not null
,CTE_dupsCleanup AS (
SELECT DISTINCT
Code,
Id,
AuditType,
ParentCode,
ParentId,
-- ROW_NUMBER() OVER(PARTITION BY ParentId, ParentCode, AuditType ORDER BY ParentId) AS Rn
FROM AUDIT_CTE
WHERE ParentCode IS NOT NULL
AND ParentId IS NOT NULL )
Then using Rn = 1 inserted only unique records into the Auidt table. Like this:
INSERT [ISSearch].[dbo].[SearchAudit] (Code, ID, AuditType, ParentCode, ParentID)
SELECT
Code,
ID,
AuditType,
ParentCode,
ParentId
FROM CTE_dupsCleanup
-- WHERE Rn = 1
END
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 have a stored proc which unions some data and returns back. At max that SP will return me 3 rows meeting the where condition.
Is there a way i can force the SP to return the blank row if there is no data present which matches the condition?
This is how my SP looks like:
SELECT Top 1 Col1, 'FirstResult' FROM Table T1
where SomeColumn='whatever'
UNION ALL
SELECT Top 1 Col2, 'SecondResult' FROM Table T1
where SomeColumn='whatever'
UNION ALL
SELECT Top 1 Col3, 'ThirdResult' FROM Table T1
where SomeColumn='whatever'
I want to always return me 3 rows regardless even if the condition doesnt match. Of course data will be empty or NULL in the resultset.
You can go here to demonstrate that the query in this answer works with no rows: http://www.sqlfiddle.com/#!3/51d1c/3
With 1 row in YourTable: http://www.sqlfiddle.com/#!3/ad1e8/1
Here is a procedure that should return what you are looking for:
CREATE PROCEDURE pExample_Get3ForcedRows
#FirstMatch VARCHAR(50)
,#SecondMatch VARCHAR(50)
,#ThirdMatch VARCHAR(50)
AS
BEGIN
DECLARE #ForceTable TABLE
(
MatchColumn VARCHAR(50) NOT NULL
)
INSERT #ForceTable (MatchColumn) VALUES (#FirstMatch),(#SecondMatch),(#ThirdMatch)
SELECT
T.Col1
, T.ResultColumn
FROM #ForceTable F
LEFT JOIN YourTable T
ON T.SomeColumn = F.MatchColumn
END
GO
DECLARE #table (Col1 varchar, String2 varchar)
IF ( (SELECT COUNT(*) FROM Table T1 WHERE SomeColumn='whatever')>0 )
INSERT INTO #table
SELECT Top 1 Col1, 'FirstResult' FROM Table T1 WHERE SomeColumn='whatever'
ELSE
INSERT INTO #table
SELECT NULL AS Col1, ''
IF ( (SELECT COUNT(*) FROM Table T1 WHERE SomeColumn='whatever')>0 )
INSERT INTO #table
SELECT Top 1 Col2, 'secondResult' FROM Table T1 WHERE SomeColumn='whatever'
ELSE
INSERT INTO #table
SELECT NULL AS Col2, ''
IF ( (SELECT COUNT(*) FROM Table T1 WHERE SomeColumn='whatever')>0 )
INSERT INTO #table
SELECT Top 1 Col3, 'thirdResult' FROM Table T1 WHERE SomeColumn='whatever'
ELSE
INSERT INTO #table
SELECT NULL AS Col3, ''
RETURN #table
Don't know if there is a more elegant version, but this should work.