I have two tables: Table A and Table B
Table A and Table B both have RowId column.
Table A and Table B both have ModifiedAt column.
Also Table A has a column called Key.
Check conditions :
Retrieve RowId's from table A if table A 'Key' = someconstant
Take those retrieved row Id's from Table A and check if ModifiedAt field of those rows is > ModifiedAT field of Table B with same rowId's.
Table B has no repetition of RowId's but Table A does.
What I tried on my own :
select *
from dbo.ResultsStored rs
WHERE HashedKey = hashbytes('MD5', #StringConcat)
and
rs.ModifiedAT > (select Max(ModifiedAt)
from dbo.Patients P
where P.RowId = rs.RowId)
Note :
Also , what surprises me is if I replace rs.RowId with hardcoded value say '1', it works but not this way.
Results when I hardcode rs.RowId :
if not exists (select * from dbo.ResultsStored RS where RS.HashedKey = 0xBBE4D4DC92C713756E6683ADD671F7DA and ModifiedAt > (select ModifiedAt from dbo.Patients where RowId = 1))
begin
print'not exists'
end
else
begin
print 'exists'
end
OUTPUT : not exists
if not exists (select * from dbo.ResultsStored RS where RS.HashedKey = 0xBBE4D4DC92C713756E6683ADD671F7DA and ModifiedAt > (select ModifiedAt from dbo.Patients where RowId = rs.RowId))
begin
print'not exists'
end
else
begin
print 'exists'
end
OUTPUT : exists
Expected output : not exists
Can I please get some help on this ?
The problem is in your data.
If I understand correctly you want to know if there are such rows in Results which date is greater then Patients date. If no such row is found then it is OK.
If so your query looks correct. You can directly select incorrect data by:
SELECT *
FROM Patients p
CROSS APPLY ( SELECT MAX(ModifiedAt) AS ModifiedAt
FROM ResultsStored rs
WHERE p.RowId = rs.RowId
) a
WHERE a.ModifiedAt > p.ModifiedAt
DECLARE #RowId INT
DECLARE CurRowId CURSOR FOR SELECT RowId FROM Patients
OPEN CurRowId
FETCH NEXT FROM CurRowId INTO #RowId
WHILE ##FETCH_STATUS = 0
BEGIN
if not exists (select * from dbo.ResultsStored where ModifiedAt >
(select ModifiedAt from dbo.Patients where RowId =
#RowId))
begin
print'not exists'
end
else
begin
print 'exists'
END
FETCH NEXT FROM CurRowId INTO #RowId
END
Related
I created a temp table #test containing 3 fields: ColumnName, TableName, and Id.
I would like to see which rows in the #test table (columns in their respective tables) are not empty? I.e., for every column name that i have in the ColumnName field, and for the corresponding table found in the TableName field, i would like to see whether the column is empty or not. Tried some things (see below) but didn't get anywhere. Help, please.
declare #LoopCounter INT = 1, #maxloopcounter int, #test varchar(100),
#test2 varchar(100), #check int
set #maxloopcounter = (select count(TableName) from #test)
while #LoopCounter <= #maxloopcounter
begin
DECLARE #PropIDs TABLE (tablename varchar(max), id int )
Insert into #PropIDs (tablename, id)
SELECT [tableName], id FROM #test
where id = #LoopCounter
set #test2 = (select columnname from #test where id = #LoopCounter)
declare #sss varchar(max)
set #sss = (select tablename from #PropIDs where id = #LoopCounter)
set #check = (select count(#test2)
from (select tablename
from #PropIDs
where id = #LoopCounter) A
)
print #test2
print #sss
print #check
set #LoopCounter = #LoopCounter + 1
end
In order to use variables as column names and table names in your #Check= query, you will need to use Dynamic SQL.
There is most likely a better way to do this but I cant think of one off hand. Here is what I would do.
Use the select and declare a cursor rather than a while loop as you have it. That way you dont have to count on sequential id's. The cursor would fetch fields columnname, id and tablename
In the loop build a dynamic sql statement
Set #Sql = 'Select Count(*) Cnt Into #Temp2 From ' + TableName + ' Where ' + #columnname + ' Is not null And ' + #columnname <> '''''
Exec(#Sql)
Then check #Temp2 for a value greater than 0 and if this is what you desire you can use the #id that was fetched to update your #Temp table. Putting the result into a scalar variable rather than a temp table would be preferred but cant remember the best way to do that and using a temp table allows you to use an update join so it would well in my opinion.
https://www.mssqltips.com/sqlservertip/1599/sql-server-cursor-example/
http://www.sommarskog.se/dynamic_sql.html
Found a way to extract all non-empty tables from the schema, then just joined with the initial temp table that I had created.
select A.tablename, B.[row_count]
from (select * from #test) A
left join
(SELECT r.table_name, r.row_count, r.[object_id]
FROM sys.tables t
INNER JOIN (
SELECT OBJECT_NAME(s.[object_id]) table_name, SUM(s.row_count) row_count, s.[object_id]
FROM sys.dm_db_partition_stats s
WHERE s.index_id in (0,1)
GROUP BY s.[object_id]
) r on t.[object_id] = r.[object_id]
WHERE r.row_count > 0 ) B
on A.[TableName] = B.[table_name]
WHERE ROW_COUNT > 0
order by b.row_count desc
How about this one - bitmask computed column checks for NULLability. Value in the bitmask tells you if a column is NULL or not. Counting base 2.
CREATE TABLE FindNullComputedMask
(ID int
,val int
,valstr varchar(3)
,NotEmpty as
CASE WHEN ID IS NULL THEN 0 ELSE 1 END
|
CASE WHEN val IS NULL THEN 0 ELSE 2 END
|
CASE WHEN valstr IS NULL THEN 0 ELSE 4 END
)
INSERT FindNullComputedMask
SELECT 1,1,NULL
INSERT FindNullComputedMask
SELECT NULL,2,NULL
INSERT FindNullComputedMask
SELECT 2,NULL, NULL
INSERT FindNullComputedMask
SELECT 3,3,3
SELECT *
FROM FindNullComputedMask
I have this SQL query:
IF NOT EXISTS (SELECT TOP 1 RowId
FROM dbo.Cache AS C
WHERE StringSearched = #pcpnpi
AND colName = 'pcpnpi'
AND ModifiedAt > (SELECT ModifiedAt
FROM dbo.Patients AS p
WHERE P.RowID = C.RowID))
BEGIN
SELECT #constVal = FunctionWeight
FROM dbo.FunctionWeights
WHERE FunctionWeights.FunctionId = 33;
INSERT INTO #Temp2
(RowNumber,ValFromUser,ColumnName,ValFromFunc,
FuncWeight,percentage)
SELECT RowNumber,#pcpnpi,'pcpnpi',PercentMatch,
#constVal,PercentMatch * #constVal
FROM dbo.Matchpcpnpi (#pcpnpi);
END
ELSE
BEGIN
INSERT INTO #Temp2
(RowNumber,ValFromUser,ColumnName,Percentage)
SELECT RowId,StringSearched,ColName,PercentMatch
FROM dbo.Cache AS C
WHERE StringSearched = #pcpnpi
AND colName = 'pcpnpi'
AND ModifiedAt > (SELECT ModifiedAt
FROM dbo.Patients AS p
WHERE P.RowID = C.RowID)
END
The above if statement is meant to avoid unnecessary look ups for strings that have already been searched earlier and MatchPercent has been calculated. In that case, it is directly retrieved from Cache table.
Above sql query is basically for one particular column and this same kind of query with just columnName and its value changing is repeated for many other columns in the procedure.
The if Exists check was obviously meant so that query performance could improve however the performance has gone down, probably because of extra checks.
Cache table which is actually meant to improve the performance, extra checks have ruined it.
Is there a way to simplify above query,please ? Any directions on same will help.
Thanks
First insert into #temp2 based on exists condition. If the Insert record count is zero then do the another insert. Try this.
INSERT INTO #Temp2
(RowNumber,ValFromUser,ColumnName,Percentage)
SELECT RowId,StringSearched,ColName,PercentMatch
FROM dbo.Cache AS C
WHERE StringSearched = #pcpnpi
AND colName = 'pcpnpi'
AND ModifiedAt > (SELECT ModifiedAt
FROM dbo.Patients AS p
WHERE P.RowID = C.RowID)
IF ##ROWCOUNT = 0
BEGIN
SELECT #constVal = FunctionWeight
FROM dbo.FunctionWeights
WHERE FunctionWeights.FunctionId = 33;
INSERT INTO #Temp2
(RowNumber,ValFromUser,ColumnName,ValFromFunc,
FuncWeight,percentage)
SELECT RowNumber,#pcpnpi,'pcpnpi',PercentMatch,
#constVal,PercentMatch * #constVal
FROM dbo.Matchpcpnpi (#pcpnpi)
END
First, consider this query in the exists:
select Top 1 RowId
from dbo.Cache as C
where StringSearched = #pcpnpi and
colName = 'pcpnpi' and
ModifiedAt > ( Select ModifiedAt FROM dbo.Patients p WHERE P.RowID = C.RowID))
For performance, you want indexes on cache(StringSearched, colName, ModifiedAt, RowId) and Patients(RowId).
However, you are running this query twice. I would suggest a structure more like:
declare #RowId . . . ; -- I don't know the type
select Top 1 #RowId = RowId
from dbo.Cache as C
where StringSearched = #pcpnpi and
colName = 'pcpnpi' and
ModifiedAt > ( Select ModifiedAt FROM dbo.Patients p WHERE P.RowID = C.RowID));
if (#RowId) is null . ..
else . . .
We are using the following trigger in SQL Server to maintain the history now I need to identify the operations just like insert,update or delete. I found some information HERE but it doesn't works with the SQL Server.
CREATE TRIGGER audit_guest_details ON [PMS].[GSDTLTBL]
FOR INSERT,UPDATE,DELETE
AS
DECLARE #SRLNUB1 INT;
DECLARE #UPDFLG1 DECIMAL(3,0);
SELECT #SRLNUB1 = I.SRLNUB FROM inserted I;
SELECT #UPDFLG1 = I.UPDFLG FROM inserted I;
BEGIN
/* Here I need to identify the operation and insert the operation type in the GUEST_ADT 3rd field */
insert into dbo.GUEST_ADT values(#SRLNUB1,#UPDFLG1,?);
PRINT 'BEFORE INSERT trigger fired.'
END;
GO
But here I need to identify the operation and want to insert operation type accordingly.
Here I don't want to create three trigger for every operations
For Inserted : Rows are in inserted only.
For Updated: Rows are in inserted and deleted.
For Deleted: Rows are in deleted only.
DECLARE #event_type varchar(42)
IF EXISTS(SELECT * FROM inserted)
IF EXISTS(SELECT * FROM deleted)
SELECT #event_type = 'update'
ELSE
SELECT #event_type = 'insert'
ELSE
IF EXISTS(SELECT * FROM deleted)
SELECT #event_type = 'delete'
ELSE
--no rows affected - cannot determine event
SELECT #event_type = 'unknown'
This is a simplified version of Mikhail's answer that uses a searched CASE expression.
DECLARE #Operation varchar(7) =
CASE WHEN EXISTS(SELECT * FROM inserted) AND EXISTS(SELECT * FROM deleted)
THEN 'Update'
WHEN EXISTS(SELECT * FROM inserted)
THEN 'Insert'
WHEN EXISTS(SELECT * FROM deleted)
THEN 'Delete'
ELSE
NULL --Unknown
END;
Since you can get multiple rows at once we do it as follows.
INSERT INTO Log_Table
(
LogDate
,LogAction
-- your field list here
,Field0
-- Example : Tracking new and old value for a specific field
-- Make sure that the [Field1_Old] is nullable or has a default value
,Field1,Field1_Old
)
SELECT
LogDate=GETDATE()
,LogAction = CASE WHEN d.[PK_Field] IS NULL THEN 'I' ELSE 'U' END
,i.Field0
,i.Field1, d.Field1
FROM inserted i
LEFT JOIN deleted d on i.[PK_Field]=d.[PK_Field]
WHERE i.[PK_Field] IS NOT NULL
INSERT INTO Log_Table
(
LogDate
,LogAction
-- your field list here
,Field0
-- Example : Tracking new and old value for a specific field
-- Make sure that the [Field1_Old] is nullable or has a default value
,Field1,Field1_Old
)
SELECT
LogDate=GETDATE()
,LogAction = 'D'
,d.Field0
,d.Field1, NULL
FROM deleted d
LEFT JOIN inserted i on i.[PK_Field]=d.[PK_Field]
WHERE i.[PK_Field] IS NULL
create trigger my_trigger on my_table
after update , delete , insert
as
declare #inserting bit
declare #deleting bit
declare #updating bit = 0
select #inserting = coalesce (max(1),0) where exists (select 1 from inserted)
select #deleting = coalesce (max(1),0) where exists (select 1 from deleted )
select #inserting = 0
, #deleting = 0
, #updating = 1
where #inserting = 1 and #deleting = 1
print 'Inserting = ' + ltrim (#inserting)
+ ', Deleting = ' + ltrim (#deleting)
+ ', Updating = ' + ltrim (#updating)
If all three are zero, there are no rows affected and I think there is no way to tell whether it is an update/delete/insert.
CREATE PROCEDURE [dbo].[SCD1] AS
-- SLOWLY CHANGING DIMENSION'S 1 (SCD1)
-- DROP PROCEDURE SCD1
-- EXEC SCD1
SET NOCOUNT ON
BEGIN
--INSERT OF NEW SOURCE VALUES INTO TEMP TABLE
SELECT SRC.* INTO #TEMP
FROM SRC_CUST SRC
LEFT OUTER JOIN DIM_CUST TGT ON SRC.CUSTOMERID = TGT.CUSTOMERID
WHERE TGT.CUSTOMERID IS NULL
--INSERT RECORDS THAT NEEDS TO BE UPDATED INTO #TEMP1 TABLE
SELECT SRC.* INTO #TEMP1
FROM SRC_CUST SRC
INNER JOIN DIM_CUST TGT ON SRC.CUSTOMERID = TGT.CUSTOMERID
WHERE (TGT.COMPANYNAME <> SRC.COMPANYNAME
OR TGT.CONTACTNAME <> SRC.CONTACTNAME
OR TGT.CONTACTTITLE <> SRC.CONTACTTITLE
OR TGT.ADDRESS <> SRC.ADDRESS
OR ISNULL(TGT.CITY,'UNK') <> SRC.CITY
OR TGT.REGION <> SRC.REGION
OR TGT.POSTALCODE <> SRC.POSTALCODE
OR TGT.COUNTRY <> SRC.COUNTRY
OR TGT.PHONE <> SRC.PHONE
OR TGT.FAX <> SRC.FAX)
--CHECK FOR THE EXISTENCE OF VALUES IN THE #TEMP TABLE
IF EXISTS(SELECT COUNT(1) FROM #TEMP)
BEGIN
--INSERT NEW RECORDS INTO THE TARGET TABLE
INSERT INTO DIM_CUST
SELECT SRC.* FROM #TEMP SRC
DROP TABLE #TEMP
PRINT 'NEW RECORDS INSERTED'
END
ELSE
DROP TABLE #TEMP
PRINT 'NO NEW RECORDS TO INSERT';
IF EXISTS(SELECT COUNT(1) FROM #TEMP1)
BEGIN
--CHECK FOR THE EXISTENCE OF VALUES IN THE #TEMP1 TABLE
-- UPDATES THE RECORDS INTO THE TARGET TABLE
UPDATE TGT
SET TGT.COMPANYNAME = SRC.COMPANYNAME
,TGT.CONTACTNAME = SRC.CONTACTNAME
,TGT.CONTACTTITLE = SRC.CONTACTTITLE
,TGT.ADDRESS = SRC.ADDRESS
,TGT.CITY = SRC.CITY
,TGT.REGION = SRC.REGION
,TGT.POSTALCODE = SRC.POSTALCODE
,TGT.COUNTRY = SRC.COUNTRY
,TGT.PHONE = SRC.PHONE
,TGT.FAX = SRC.FAX
FROM DIM_CUST TGT
INNER JOIN #TEMP1 SRC ON TGT.CUSTOMERID = SRC.CUSTOMERID
DROP TABLE #TEMP1
PRINT 'UPDATED RECORDS'
END
ELSE
DROP TABLE #TEMP1
PRINT 'NO RECORDS THERE TO UPDATE'
END
The problem that I get when execute this stored procedure is that it goes into the else part as well even though the if condition is satisfied. Can any one help me debug this stored procedure.
The source table that I have taken is the Customer table in the Northwind database.
Thanks.
There are 2 problems
You need to BEGIN/END the ELSE clauses too. Only the DROP is being excecuted in the ELSE : the PRINT *always" is which makes it run like you've reported
...
ELSE
BEGIN
DROP TABLE #TEMP
PRINT 'NO NEW RECORDS TO INSERT';
END
...
ELSE
BEGIN
DROP TABLE #TEMP1
PRINT 'NO RECORDS THERE TO UPDATE'
END
Secondly, these are always true
IF EXISTS(SELECT COUNT(1) FROM #TEMP)
...
IF EXISTS(SELECT COUNT(1) FROM #TEMP1)
All you need is.
IF EXISTS(SELECT * FROM #TEMP)
See these answers from me to explain why:
Does COUNT(*) always return a result?
What's the best to check if item exist or not: Select Count(ID)OR Exist(...)?
I have a simple query for update table (30 columns and about 150 000 rows).
For example:
UPDATE tblSomeTable set F3 = #F3 where F1 = #F1
This query will affected about 2500 rows.
The tblSomeTable has a trigger:
ALTER TRIGGER [dbo].[trg_tblSomeTable]
ON [dbo].[tblSomeTable]
AFTER INSERT,DELETE,UPDATE
AS
BEGIN
declare #operationType nvarchar(1)
declare #createDate datetime
declare #UpdatedColumnsMask varbinary(500) = COLUMNS_UPDATED()
-- detect operation type
if not exists(select top 1 * from inserted)
begin
-- delete
SET #operationType = 'D'
SELECT #createDate = dbo.uf_DateWithCompTimeZone(CompanyId) FROM deleted
end
else if not exists(select top 1 * from deleted)
begin
-- insert
SET #operationType = 'I'
SELECT #createDate = dbo..uf_DateWithCompTimeZone(CompanyId) FROM inserted
end
else
begin
-- update
SET #operationType = 'U'
SELECT #createDate = dbo..uf_DateWithCompTimeZone(CompanyId) FROM inserted
end
-- log data to tmp table
INSERT INTO tbl1
SELECT
#createDate,
#operationType,
#status,
#updatedColumnsMask,
d.F1,
i.F1,
d.F2,
i.F2,
d.F3,
i.F3,
d.F4,
i.F4,
d.F5,
i.F5,
...
FROM (Select 1 as temp) t
LEFT JOIN inserted i on 1=1
LEFT JOIN deleted d on 1=1
END
And if I execute the update query I have a timeout.
How can I optimize a logic to avoid timeout?
Thank you.
This query:
SELECT *
FROM (
SELECT 1 AS temp
) t
LEFT JOIN
INSERTED i
ON 1 = 1
LEFT JOIN
DELETED d
ON 1 = 1
will yield 2500 ^ 2 = 6250000 records from a cartesian product of INSERTED and DELETED (that is all possible combinations of all records in both tables), which will be inserted into tbl1.
Is that what you wanted to do?
Most probably, you want to join the tables on their PRIMARY KEY:
INSERT
INTO tbl1
SELECT #createDate,
#operationType,
#status,
#updatedColumnsMask,
d.F1,
i.F1,
d.F2,
i.F2,
d.F3,
i.F3,
d.F4,
i.F4,
d.F5,
i.F5,
...
FROM INSERTED i
FULL JOIN
DELETED d
ON i.id = d.id
This will treat update to the PK as deleting a record and inserting another, with a new PK.
Thanks Quassnoi, It's a good idea with "FULL JOIN". It is helped me.
Also I try to update table in portions (1000 items in one time) to make my code works faster because for some companyId I need to update more than 160 000 rows.
Instead of old code:
UPDATE tblSomeTable set someVal = #someVal where companyId = #companyId
I use below one:
declare #rc integer = 0
declare #parts integer = 0
declare #index integer = 0
declare #portionSize int = 1000
-- select Ids for update
declare #tempIds table (id int)
insert into #tempIds
select id from tblSomeTable where companyId = #companyId
-- calculate amount of iterations
set #rc=##rowcount
set #parts = #rc / #portionSize + 1
-- update table in portions
WHILE (#parts > #index)
begin
UPDATE TOP (#portionSize) t
SET someVal = #someVal
FROM tblSomeTable t
JOIN #tempIds t1 on t1.id = t.id
WHERE companyId = #companyId
delete top (#portionSize) from #tempIds
set #index += 1
end
What do you think about this? Does it make sense? If yes, how to choose correct portion size?
Or simple update also good solution? I just want to avoid locks in the future.
Thanks