CTE with insert and update in sql server 2008 [duplicate] - sql

This question already has answers here:
How can I reuse a Common Table Expression
(2 answers)
Closed 8 years ago.
I am using SQL Server 2008 and I want to know if I can use CTE with insert and update together e.g. if one condition fails then insert else update.
With X As
(
Select COMP
From comp
)
IF EXISTS ( Select * From A Where A.COMP = X.COMP )
Insert Into A (COMP,comp1)
select X.COMP, #COMP1 from X
Else
Update A set comp1=#comp1
where comp= X.Comp
I have tried to use
With X As
(
Select COMP
From comp
)
Insert Into A (COMP,comp1)
select X.COMP, #COMP1 from X
With Y As
(
Select COMP
From comp
)
Update A set comp1=#comp1
join Y on comp= Y.Comp
But if it is a insert then it is inserting the record and also it is executing the Update block . Can you tell me how to avoid that with If statement.

As you can only use CTE once after it is declared, you cannot used it in both INSERT and UPDATE statements.
As you cannot use MERGE due to constraints out of your control, as you say, you can use a temporary table instead:
Select COMP
into #temp
From comp
IF EXISTS ( Select * From A Where A.COMP = #temp.COMP )
Insert Into A (COMP,comp1)
select #temp.COMP, #COMP1 from #temp
Else
Update A set comp1=#comp1 where comp= #temp.Comp

A WITH clause can only be used with a single SELECT, INSERT, UPDATE or DELETE statement. It cannot be used with an IF or any other statement. That you've already been told in comments.
The reason why your second attempt, where you are trying to use separate WITH clauses for INSERT and UPDATE, fails is probably because there is no semicolon at the end of the INSERT statement. The WITH keyword has multiple uses in Transact-SQL and sometimes it is expected in the middle of the statement while at other times it goes at the beginning. To avoid ambiguity, a semicolon is made mandatory (in SQL Server 2008 and later version) at the end of a statement if the next statement begins with a WITH clause.
But actually it would be a good idea to get into the habit of using semicolons anyway.
So, this is how you should run these two statements together:
With X As
(
Select COMP
From comp
)
Insert Into A (COMP,comp1)
select X.COMP, #COMP1 from X; /* <-- this semicolon is required */
With Y As
(
Select COMP
From comp
)
Update A set comp1=#comp1
join Y on comp= Y.Comp; /* <-- this one is optional,
but why not make things consistent? */

Related

Snowfalke sql update if exists else insert

I have looked at other question but seems that snowflake doesn't support if/else in sql, at least not the way that other sql servers support it.
some suggested to use javascript but i would like to avoid that if I can.
I am trying to Insert into a table using snowflake python library some data, if it's already there then I would like to update the data, I have looked at merge but it doesn't seem to fit me since my data isn't a table
that's what I have so far that isn't working
f"""BEGIN
IF (EXISTS (SELECT * FROM {self.okr_table} WHERE kpi=TRUE AND Month(month)=MONTH(current_date()) AND year(month)=YEAR(current_date())))
THEN
UPDATE {self.okr_table} SET [DATA] = {json.dumps(self.data)} WHERE kpi=TRUE AND Month(month)=MONTH(current_date()) AND year(month)=YEAR(current_date()))
ELSE
INSERT INTO {self.okr_table} (month, data, kpi) SELECT current_date(),parse_json('{json.dumps(self.data)}'), true;
END"""
To perfrom INSERT/UPDATE it is better to use single MERGE statement
I have looked at merge but it doesn't seem to fit me since my data isn't a table
It is not an issue as source could be a table or subquery:
MERGE INTO {self.okr_table}
USING (SELECT PARSE_JSON({json.dumps(self.data)} AS data
, MONTH(current_date()) AS month
, YEAR(current_date()) AS year
) s
ON {self.okr_table}.KPI
AND MONTH({self.okr_table}.month) = s.month
AND YEAR({self.okr_table}.month) = s.year
WHEN MATCHED THEN UPDATE
WHEN NOT MATCHED THEN INSER ...;
IF/ELSE branching works in Snowflake:
BEGIN
IF (EXISTS (...)) THEN
UPDATE ... ;
ELSE
INSERT ... ;
END IF;
END;
Please note ; after each statement, END IF and parenthesis around condition.

Convert SQL to PL/SQL For "IF NOT EXISTS"

I have some SQL code that I want to convert into PL/SQL for Oracle, however I'm struggling to find a good alternative for the "IF NOT EXISTS" function. Can someone help?
Here's the SQL that needs converting:
IF NOT EXISTS (SELECT * FROM SEC_ConfigSetting WHERE ItemKey='Version')
BEGIN
INSERT INTO SEC_ConfigSetting
([VersionNo]
,[Section]
,[ItemKey]
,[ItemValue]
,[ItemType])
VALUES
(1,4,'Version','V1.4.0',0)
END
ELSE
BEGIN
/* Update database version */
UPDATE [SEC_ConfigSetting]
SET [ItemValue] = 'V1.4.0'
WHERE ItemKey='Version'
END
What you're doing looks like you need merge
merge into SEC_ConfigSetting s
using (select 1 VersionNo, 4 Section,'Version' ItemKey,'V1.4.0' ItemValue, 0 ItemType from dual) d
on (s.ItemKey = d.ItemKey)
when matched then update set
ItemValue = d.ItemValue
when not matched then
INSERT (VersionNo, Section,ItemKey,ItemValue,ItemType)
VALUES (d.VersionNo, d.Section, d.ItemKey, d.ItemValue, d.ItemType);
And this can be used as a plain SQL or as part of PL/SQL script.

How to update two different tables using a single UPDATE statement

I am trying to update two tables with a single statement like this
db2 update catentry, catentdesc
set catentry.buyable=0,
catentdesc.published=0
where catentry.catentry_id=catentdesc.catentry_id
and catentry.partnumber='some value'
but getting an error
How can I accomplish this?
Set DB2 CLP to autocommit OFF and use ; command terminator:
db2 +c -t
update catentry set buyable=0 where partnumber='some value';
update catentdesc set published=0 where catentry_id IN
(SELECT catentry_id FROM catentry where partnumber='some value');
commit;
Not sure why you want to do this in a single statement, but recent versions of DB2 for LUW (and, with some restrictions, DB2 for z/OS) support what is called data change table references that allow tricks like
WITH t1 (catentry_id) AS (
SELECT catentry_id FROM FINAL TABLE (
UPDATE catentry SET buyable = 0 WHERE partnumber = 'some value'
)
), t2 (catentry_id) AS (
SELECT catentry_id FROM FINAL TABLE (
UPDATE catentdesc SET published = 0 WHERE catentry_id = (
SELECT catentry_id FROM t1
)
)
) SELECT 1 FROM sysibm.sysdummy1
Note that the actual statement must be a SELECT from something, and UPDATE statements are also wrapped in their own SELECTs, but this is executed as a single statement.

Create a function for generating random number in SQL Server trigger

I have to create a function in a SQL Server trigger for generating random numbers after insert. I want to update the column with that generated random number please help what I have missed in my code.
If you know other ways please suggest a way to complete my task.
This my SQL Server trigger:
ALTER TRIGGER [dbo].[trgEnquiryMaster]
ON [dbo].[enquiry_master]
AFTER INSERT
AS
declare #EnquiryId int;
declare #ReferenceNo varchar(50);
declare #GenReferenceNo NVARCHAR(MAX);
select #EnquiryId = i.enquiry_id from inserted i;
select #ReferenceNo = i.reference_no from inserted i;
BEGIN
SET #GenReferenceNo = 'CREATE FUNCTION functionRandom (#Reference VARCHAR(MAX) )
RETURNS VARCHAR(MAX)
As
Begin
DECLARE #r varchar(8);
SELECT #r = coalesce(#r, '') + n
FROM (SELECT top 8
CHAR(number) n FROM
master..spt_values
WHERE type = P AND
(number between ascii(0) and ascii(9)
or number between ascii(A) and ascii(Z)
or number between ascii(a) and ascii(z))
ORDER BY newid()) a
RETURNS #r
END
'
EXEC(#GenReferenceNo)
-- SET NOCOUNT ON added to prevent extra result sets from
-- interfering with SELECT statements.
SET NOCOUNT ON
-- update statements for trigger here
UPDATE enquiry_master
SET reference_no ='updated'
WHERE enquiry_id = #EnquiryId
END
To generate random numbers, just call CRYPT_GEN_RANDOM which was introduced in SQL Server 2008:
SELECT CRYPT_GEN_RANDOM(5) AS [Hex],
CONVERT(VARCHAR(20), CRYPT_GEN_RANDOM(5), 2) AS [HexStringWithout0x],
CONVERT(VARCHAR(20), CRYPT_GEN_RANDOM(10)) AS [Translated-ASCII],
CONVERT(NVARCHAR(20), CRYPT_GEN_RANDOM(20)) AS [Translated-UCS2orUTF16]
returns:
Hex HexStringWithout0x Translated-ASCII Translated-UCS2orUTF16
0x4F7D9ABBC4 0ECF378A7A ¿"bü<ݱØï 붻槬㟰添䛺⯣왚꒣찭퓚
If you are ok with just 0 - 9 and A - F, then the CONVERT(VARCHAR(20), CRYPT_GEN_RANDOM(5), 2) is all you need.
Please see my answer on DBA.StackExchange on a similar question for more details:
Password generator function
The UPDATE statement shown in the "Update" section of that linked answer is what you want, just remove the WHERE condition and add the JOIN to the Inserted pseudo-table.
The query should look something like the following:
DECLARE #Length INT = 10;
UPDATE em
SET em.[reference_no] = rnd.RandomValue
FROM dbo.enquiry_master em
INNER JOIN Inserted ins
ON ins.enquiry_id = em.enquiry_id
CROSS APPLY dbo.GenerateReferenceNo(CRYPT_GEN_RANDOM((em.[enquiry_id] % 1) + #Length)) rnd;
And since the function is slightly different, here is how it should be in order to get both upper-case and lower-case letters:
CREATE FUNCTION dbo.GenerateReferenceNo(#RandomValue VARBINARY(20))
RETURNS TABLE
WITH SCHEMABINDING
AS RETURN
WITH base(item) AS
(
SELECT NULL UNION ALL SELECT NULL UNION ALL SELECT NULL UNION ALL
SELECT NULL UNION ALL SELECT NULL UNION ALL SELECT NULL
), items(item) AS
(
SELECT NULL
FROM base b1
CROSS JOIN base b2
)
SELECT (
SELECT TOP (LEN(#RandomValue))
SUBSTRING('1234567890QWERTYUIOPASDFGHJKLZXCVBNMqwertyuiopasdfghjklzxcvbnm',
(CONVERT(TINYINT, SUBSTRING(#RandomValue, 1, 1)) % 62) + 1,
1) AS [text()]
FROM items
FOR XML PATH('')
) AS [RandomReferenceNo];
GO
And please follow the usage shown above, passing in CRYPT_GEN_RANDOM((em.[enquiry_id] % 1) + #Length), not: CRYPT_GEN_RANDOM(#RefferenceNOLength).
Other notes:
#marc_s already explained the one-row vs multiple-rows flaw and how to fix that.
not only is a trigger not the place to create a new object (i.e. the function), that function wouldn't have worked anyway since the call to newid() (in the ORDER BY) is not allowed in a function.
You don't need to issue two separate SELECTs to set two different variables. You could do the following:
SELECT #EnquiryId = i.enquiry_id,
#ReferenceNo = i.reference_no
FROM TableName i;
Passing strings into a function requires quoting those strings inside of single-quotes: ASCII('A') instead of ASCII(A).
UPDATE
The full Trigger definition should be something like the following:
ALTER TRIGGER [dbo].[trgEnquiryMaster]
ON [dbo].[enquiry_master]
AFTER INSERT
AS
BEGIN
DECLARE #Length INT = 10;
UPDATE em
SET em.[reference_no] = rnd.RandomValue
FROM dbo.enquiry_master em
INNER JOIN Inserted ins
ON ins.enquiry_id = em.enquiry_id
CROSS APPLY dbo.GenerateReferenceNo(
CRYPT_GEN_RANDOM((em.[enquiry_id] % 1) + #Length)
) rnd;
END;
A trigger should be very nimble and quick - it is no place to do heavy and time-intensive processing, and definitely no place to create new database objects since (a) the trigger is executed in the context of the code causing it to fire, and (b) you cannot control when and how often the trigger is fired.
You need to
define and create your function to generate that random value during database setup - once, before any operations are executed on the database
rewrite your trigger to take into account that multiple rows could be inserted at once, and in that case, the Inserted table will contain multiple rows which all have to be handled.
So your trigger will look something like this (with several assumptions by me - e.g. that enquiry_id is the primary key on your table - you need this to establish the INNER JOIN between your data table and the Inserted pseudo table:
ALTER TRIGGER [dbo].[trgEnquiryMaster]
ON [dbo].[enquiry_master]
AFTER INSERT
AS
-- SET NOCOUNT ON added to prevent extra result sets from
-- interfering with SELECT statements.
SET NOCOUNT ON
-- update statements for trigger here
UPDATE enq
SET reference_no = dbo.GenerateRandomValue(.....)
FROM enquiry_master enq
INNER JOIN inserted i ON enq.enquiry_id = i.enquiry_id

Keyword 'Values' absent Oracle 9i

I'm trying to execute the following trigger when inserting some data in my table :
CREATE OR REPLACE TRIGGER AJOUTER_NEW_CONSISTANCE
AFTER INSERT OR UPDATE OF
CONSISTANCE_LIBELLE_1,CONSISTANCE_LIBELLE_2,CONSISTANC_LIBELLE_3
ON DB.ETAT_PARCELLAIRE
BEGIN
insert into DB.CONSISTANCE.LIBELLE
select consistance_libelle_1
from DB.ETAT_PARCELLAIRE
where DB.ETAT_PARCELLAIRE.consistance_libelle_1 not in (
select LIBELLE from DB.CONSISTANCE.LIBELLE);
END;
But it keeps giving me the following error :
PL/SQL : ORA00926 : Keyword Values absent.
How can I fix this ?
Thank you for help in advance :)
If CONSISTANCE is a table with a column called LIBELLE then you're referring to it incorrectly.
your insert is including the column, which I assume means the table has other columns and you only want to insert a value into that one, but your syntax is wrong (DB.CONSISTANCE.LIBELLE should be DB.CONSISTANCE(LIBELLE)). it is this line that's generating the ORA-00926.
your sub-select is including the column in the table name (DB.CONSISTANCE.LIBELLE should be just DB.CONSISTANCE)
So it should be:
CREATE OR REPLACE TRIGGER AJOUTER_NEW_CONSISTANCE
AFTER INSERT OR UPDATE OF
CONSISTANCE_LIBELLE_1,CONSISTANCE_LIBELLE_2,CONSISTANC_LIBELLE_3
ON DB.ETAT_PARCELLAIRE
BEGIN
insert into DB.CONSISTANCE(LIBELLE)
select consistance_libelle_1
from DB.ETAT_PARCELLAIRE
where consistance_libelle_1 not in (
select LIBELLE from DB.CONSISTANCE);
END;
I'm also not sure if CONSISTANC_LIBELLE_3 is a typo and it should be CONSISTANCE_LIBELLE_3.
You could also do a not exists instead of a not in:
insert into DB.CONSISTANCE(LIBELLE)
select CONSISTANCE_LIBELLE_1
from DB.ETAT_PARCELLAIRE
where not exists (
select 1
from DB.CONSISTANCE
where LIBELLE = DB.ETAT_PARCELLAIRE.CONSISTANCE_LIBELLE_1
);
Or use a merge:
merge into DB.CONSISTANCE c
using (select CONSISTANCE_LIBELLE_1 from DB.ETAT_PARCELLAIRE) ep
on (c.LIBELLE = ep.CONSISTANCE_LIBELLE_1)
when not matched then
insert (LIBELLE) values (ep.CONSISTANCE_LIBELLE_1);
Using a trigger to (partially) maintain that table looks odd though - it would be simpler to have a view which selects distinct values from ETAT_PARCELLAIRE:
create or replace view CONSISTANCE_VIEW as
select distinct CONSISTANCE_LIBELLE_1
from ETAT_PARCELLAIRE;
But they would have different content - once a value has appeared in CONSISTANCE_LIBELLE_1 it will always remain in CONSISTANCE as you are not removing defunct values, only inserting new ones; whereas CONSISTANCE_VIEW would only show values currently in the table. It isn't clear which behaviour you want.