UPDATE Select Statement with Multiple Joins - sql-server-2005

I am trying to update data in a Contacts_CSTM table based on data in a Project_CSTM table. This is the query I'm using, but I get an error: "Conversion failed when converting from a character string to uniqueidentifier"
ALTER PROCEDURE Insurance_Check_Expiration
#ID_C AS NVARCHAR (55) = ID_C
AS
BEGIN
SET NOCOUNT ON
IF EXISTS(SELECT * FROM CONTACTS_CSTM WHERE ID_C = #ID_c)
Update contacts_cstm set insurance_expired_label_c = 'INSURANCE EXPIRED'
WHERE DRIVERS_LICENSE_NUMBER_C IS NOT NULL AND #ID_C=
(SELECT cc.id_c
FROM PROJECT_CSTM PC
JOIN PROJECT P
ON P.ID = PC.ID_C
JOIN PROJECT_RELATION PR
ON PR.PROJECT_ID = P.ID
JOIN CONTACTS C
ON C.ID = PR.RELATION_ID
JOIN CONTACTS_CSTM CC
ON CC.ID_C = C.ID
WHERE CC.ID_C = #ID_C AND INSURANCE_EXPIRED_C ='1')
Thanks.

For one, you're setting the value of #ID_C to a value (ID_C) which is obviously not a valid GUID.
This is the functional equivalent of what you did, run it and you'll get the same error.
CREATE PROCEDURE Insurance_Check_Expiration
#ID_C AS NVARCHAR (55) = ID_C
AS
BEGIN
DECLARE #A UNIQUEIDENTIFIER
SET #A = #ID_C
END
exec Insurance_Check_Expiration
EDIT: Here's a functional example based on the OP's comments:
CREATE TABLE GUIDExample (ID_C UNIQUEIDENTIFIER)
GO
CREATE PROC GuidExample_Insert #ID_C UNIQUEIDENTIFIER
AS
BEGIN
SELECT #ID_C
END
GO
CREATE TRIGGER GUID_Example ON GUIDExample
AFTER INSERT
AS
DECLARE #ID_C UNIQUEIDENTIFIER
SELECT #ID_C = ID_C
FROM Inserted
EXEC GuidExample_Insert #ID_C
GO
DECLARE #SampleGUID UNIQUEIDENTIFIER
SET #SampleGUID = NEWID()
INSERT GUIDExample (ID_C)
VALUES(#SampleGUID)

Related

UDF don't give the same result

when I exececute the UDF out of the trigger the result is not the same
the UDF return always true when executing in the trigger
but out of the trigger the result is true or false
ALTER FUNCTION [dbo].[MandatExist]
(
#Numero int,
#IdBranche int,
#Exercice int
)
RETURNS bit
AS
BEGIN
DECLARE #Result bit
DECLARE #Nbr int
DECLARE #Categ int
SELECT #Categ = CategorieNumero
FROM Branche
WHERE IdBranche = #IdBranche
SELECT #Nbr=COUNT(*)
FROM Mandat AS M INNER JOIN Branche AS B ON M.IdBranche=B.IdBranche
WHERE (Numero = #Numero) AND (B.CategorieNumero = #Categ) AND (Exercice = #Exercice)
IF #Nbr = 0
SET #Result = 0
ELSE
SET #Result = 1
RETURN #Result
END
the trigger call MandatExist to get if the number exist or not
ALTER TRIGGER [dbo].[ValidInsertUpdate_Mandat]
ON [dbo].[Mandat]
FOR INSERT,UPDATE
AS
BEGIN
SET NOCOUNT ON;
DECLARE #Cloturer AS bit
DECLARE #Exercice AS int
DECLARE #IdBranche AS int
DECLARE #Numero AS int
DECLARE #Message AS nvarchar(100)
SELECT #Cloturer=Cloturer, #Exercice=Exercice, #Numero=Numero, #IdBranche=IdBranche
FROM INSERTED
IF (dbo.MandatExist(#Numero, #IdBranche, #Exercice)=1)
BEGIN
SET #Message = 'Numero de mandat existant.'
RAISERROR(#Message, 16, 1)
ROLLBACK TRAN
END
INSERTED is a table, thus may contain more than one row which means that this code
SELECT #Cloturer=Cloturer, #Exercice=Exercice, #Numero=Numero, #IdBranche=IdBranche
FROM INSERTED
is essentially incorrect.
UDFs are not the best choice for set-based programming and may cause performance degradation. Specifically this UDF makes absolutely no sense, has no reason for encapsulating this code into separate module. This is a trivial EXISTS code.
Entire function can and must be replaced by EXISTS statement, the whole code of the trigger might look like:
IF EXISTS(
SELECT 1
FROM INSERTED
INNER JOIN ...
WHERE ...
)
BEGIN
RAISERROR(...)
END
I'm not sure what's the meaning of your tables and columns, but I assume you are trying to check some uniqueness. So, assuming that you don't want another Mandat record for the same CategorieNumero, the final EXISTS might look like:
IF EXISTS(
SELECT 1
FROM INSERTED i
INNER JOIN Branch b on b.IdBranche = i.IdBranch
-- other branches with the same CategorieNumero; why isn't CategorieNumero unique?
INNER JOIN Branch b_dup on b_dup.CategorieNumero = b.CategorieNumero
-- existing Mandat rows for the same CategorieNumero with any IdBranch
INNER JOIN Mandat m_dup on m_dup = b_dup.IdBranch
-- ensure you're not comparing inserted/updated Mandat row to itself
WHERE i.ID != m_dup.ID
)
...
But your intent is unclear to me and I think that after clarification most of your needs will be easily satisfied by unique constraints.
If you don't want more that one row for each set of (Exercice, Numero, IdBranch) - just add a unique constraint to Mandat table and drop both trigger and function!
Don't overuse triggers. And UDFs.
i have used the solution of Ivan
IF EXISTS(
SELECT 1
FROM INSERTED I INNER JOIN Branche b ON b.IdBranche = i.IdBranche
INNER JOIN Branche b_dup ON b_dup.IdBranche = b.IdBranche
INNER JOIN Mandat m_dup on (m_dup.Exercice = i.Exercice) AND (m_dup.Numero = i.Numero) AND (b_dup.IdBranche=i.IdBranche)
WHERE i.IdMandat != m_dup.IdMandat
)
BEGIN
RAISERROR('error', 16, 1)
ROLLBACK TRAN
END

Update SQL Server stored procedure - switch columns in the parameters

I am trying to create a basic stored procedure as below:
SET ANSI_NULLS ON
GO
SET QUOTED_IDENTIFIER ON
GO
ALTER PROCEDURE [dbo].[spUpdate1]
#registration_id NCHAR(8),
#registration_status INT
AS
BEGIN
UPDATE dbo.Test1
SET Registration_Status = #registration_status
WHERE Registration_id = #registration_id;
END
This stored procedure works without issue, however the registration_id isn't the parameter I would like to pass in.
I have a three columns in my table:
Reference_Number, Registration_id, Registration_Status
I would like to be able to pass in the Reference_Number as the parameter, from here the statement selects the registration_id from the relevant row in the table, then this is passed to the update statement to perform the update
Based on your comment you can probably do this;
ALTER PROC [dbo].[spUpdate1]
#registration_status int,
#reference_number nvarchar(max)
AS
BEGIN
UPDATE t1
SET registration_status = #registration_status
FROM dbo.Test1 t1
WHERE CAST(t1.reference_number as NVARCHAR(MAX)) = #reference_number
END
Although I recommend changing the data type of Reference_Number to nvarchar(max) instead of text...
this is what you wanted ? based on Reference_Number to get the Registration_id ?
ALTER PROC [dbo].[spUpdate1]
#registration_id nchar(8),
#registration_status int,
#Reference_Number nchar(8) -- added this
AS
BEGIN
update t2
set Registration_Status = #registration_status
from dbo.Test1 t1
inner join dbo.Test1 t2 on t1.Registration_id = t2.Registration_id
where t1.Reference_Number = #Reference_Number
END

How to create procedure with multiple string select query in sql?

I want to create procedure with multiple string select query.I want to insert data to table variable and join that temp table with other table.
I don't want to create temp table as real tables. I want to insert data to memory temp table.
Here is my procedure,
CREATE PROCEDURE sp_TempBatch
AS
DECLARE #TempBatchSerial TABLE
(
ID int,
Name nvarchar(200),
StockType nvarchar(50),
ItemNo nvarchar(50)
)
DECLARE #TempQuery as nvarchar(MAX)='',
#VendorQuery as nvarchar(MAX)=''
BEGIN
SET #TempQuery='SELECT ID,Name,'
IF StockType = '1'
BEGIN
SET #TempQuery += ' ''Batch'' as StockType,'
END
ELSE
BEGIN
SET #TempQuery += ' ''Serial'' as StockType,'
END
SET #TempQuery += 'ItemNo INTO #TempBatchSerial
FROM Stock'
EXEC (#TempQuery)
SET #VendorQuery+=' SELECT #TempBatchSerial.* FROM #TempBatchSerial
INNER JOIN Vendor
ON #TempBatchSerial.ID = Vendor.ID
INNER JOIN Partner
ON Vendor.parentid = Partner.syskey'
EXEC (#VendorQuery)
END
When execute procedure show error message of Must declare the table variable "#TempBatchSerial"
You have to refer to #tempBatchSerial via an Alias
That's the only way #tempTables can be referred or linked to.
SELECT T.* FROM #TempBatchSerial T
INNER JOIN Vendor
ON T.ID = Vendor.ID
INNER JOIN Partner
ON Vendor.parentid = Partner.syskey
If that doesn't work you can try to put the #tempTable in the #vendorQuery text.

Modify SQL trigger to work when inserted table contains more than one row

I've got a SQL trigger written for a table in SQL Server 2008. It works well when there is only one row in the 'inserted' table. How can I modify this trigger to work correctly when there are multiple rows? Performance is key here, so I'd like to stay away from cursors, temp tables, etc. (if possible).
Essentially the trigger checks to see if either the 'ClientID' or 'TemplateID' fields were changed. If they were, and the OriginalClientID or OriginalTemplateID fields are null, it populates them (thus setting the OriginalXXX fields once and only once so I can always see what the first values were).
CREATE TRIGGER [dbo].[trigSetOriginalValues]
ON [dbo].[Review]
FOR INSERT, UPDATE
AS
BEGIN
IF (NOT UPDATE(TemplateID) AND NOT UPDATE(ClientID)) return
DECLARE #TemplateID int
DECLARE #OriginalTemplateID int
DECLARE #ClientID int
DECLARE #OriginalClientID int
DECLARE #ReviewID int
SET #ReviewID = (SELECT ReviewID FROM inserted)
SET #ClientID = (SELECT ClientID FROM inserted)
SET #TemplateID = (SELECT TemplateID FROM inserted)
SET #OriginalTemplateID = (SELECT OriginalTemplateID FROM inserted);
SET #OriginalClientID = (SELECT OriginalClientID FROM inserted);
IF (#OriginalTemplateID IS NULL AND #TemplateID IS NOT NULL)
BEGIN
UPDATE [dbo].[Review] SET OriginalTemplateID = #TemplateID WHERE ReviewID=#ReviewID
END
IF (#OriginalClientID IS NULL AND #ClientID IS NOT NULL)
BEGIN
UPDATE [dbo].[Review] SET OriginalClientID = #ClientID WHERE ReviewID=#ReviewID
END
END
This should be your trigger:
UPDATE A
SET A.OriginalTemplateID = B.TemplateID
FROM [dbo].[Review] A
INNER JOIN INSERTED B
ON A.ReviewID = B.ReviewID
WHERE A.OriginalTemplateID IS NULL AND B.TemplateID IS NOT NULL
UPDATE A
SET A.OriginalClientID = B.ClientID
FROM [dbo].[Review] A
INNER JOIN INSERTED B
ON A.ReviewID = B.ReviewID
WHERE A.OriginalClientID IS NULL AND B.ClientID IS NOT NULL
Though you could still do this on a single UPDATE, but with a more complicated filter.

SQL: Query timeout expired

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