I created a trigger for insert on a table :
CREATE TRIGGER [General].[trg_Extr_Tab1_Insert]
ON [General].[Extr_Tab1]
AFTER INSERT
AS
BEGIN
declare #nExtractions int;
SET NOCOUNT ON;
begin try
set #nExtractions = (select count(*) from General.Extr_Tab1 )
create table #Extr
(
Extr1 int
)
.....
the trigger gets added to the table (Extr_Tab1) and works fine .
Since I have many tables like Extr_Tab1 (Extr_Tab1, Extr_Tab2 ... Extr_Tabn) I want to create a trigger for each one of these tables. Having a script which creates the database and the tables , I wanted to add the scripts for creating the triggers in the general script, right after the creation of the tables.
But when i run the general script, I get a :
Incorrect syntax near the keyword 'TRIGGER'.
right on the first "CREATE TRIGGER " line .
The code for creating the trigger has been generated automatically on Sql Server : I took the code, which was working fine , and duplicated it just changing the table names :
CREATE TRIGGER [General].[trg_Extr_Tabn_Insert]
ON [General].[Extr_Tabn]
AFTER INSERT
AS
BEGIN
declare #nExtractions int;
create table #Extr ( Extr1 int )
....
Moreover, I am using temp tables in the script (#Extr) , but it seems even if it is defined each time in the respective trigger's scope, whenever the script defines it I get an error :
There is already an object named '#Extr' in the database.
like if the definition of the temp table is global no matter if it is defined in the scope of the trigger
Related
I have a database creation script that sets up tables, stored procedures, views, etc. When I change the type of a column in a create table statement, I want this change to be reflected in the create stored procedures / views / etc statements that reference that table without having to go through and manually change each one.
In other words I want my stored procedures to automatically determine the column type based on another column's type on creation. I don't need this to work on a live database with data, just while I'm iterating over the design and prototyping.
Something like a TYPE_OF() in this (fictional) example:
create table Logs
(
id int identity(1, 1) primary key,
userName varchar(32),
logType int foreign key references LogType(id),
description varchar(128),
datestamp datetime
);
go
create procedure WriteLog
(
#userName TYPE_OF(Logs.userName), -- should be varchar(32),
#logType int,
#description TYPE_OF(Logs.description) -- should be varchar(128)
)
as
begin
insert into Logs
values(#userName, #logType, #description, SYSDATETIME());
end
go;
I think I remember something similar from Oracle / SQL Plus / PLSQL but I am having trouble finding it.
I'm using SQL Server Management Studio v18.4
Not sure if the TYPEOF feature you're looking for exitsts, but you could try and use a DDL Trigger to keep your procedure in sync with the column type changes.
This trigger would get fired every time a table is altered and you'd just have to parse the EVENTDATA() to see if the column types in the Logs table have changed. The body of your trigger would look something like this:
CREATE TRIGGER OnLogsChanged
ON DATABASE
FOR ALTER_TABLE
AS
BEGIN
-- 1. Parse EVENTDATA() to see if the Logs table was altered
-- 2. If it has, store the definition of the WriteLog procedure into a variable by reading it from sys.procedures
-- 3. Read the new types for the columns of the Logs table from sys.all_columns
-- 4. replace the parameter declarations in the procedure definition to match the new types in the Logs table
-- 5. alter the procedure with the new definition by building up the ALTER PROCEDURE statement as a string and executing it with sp_executesql
END
As long as the trigger stays enabled your procedure should stay in sync with the table column types.
I'm trying to create a stored procedure that will insert data stored in a view in a table.
I used this script :
use SVC_DWH
Go
SET ANSI_NULLS ON
GO
INSERT INTO DWH_SOVAC_PROD_KIT_LIFE_CYCLE
SELECT *
FROM TD_Importation_Kit_Production_Vente_VH
The problem is that my table is underlined and it tells me that there no invalid object name like that even though my database contains that table.
Try use exactly columns name to insert
INSERT INTO DWH_SOVAC_PROD_KIT_LIFE_CYCLE
VALUES([Field1],[Field2],[Field3],...)
SELECT [Column1],[Column2],[Column3],...
FROM TD_Importation_Kit_Production_Vente_VH
I am trying to add a temporary column to a target table and use that column in a where clause to insert new data into a parent table via stored procedure that I am using for a one-to-one relationship from parent to target table (see code below). I am getting an error with the alter table add column statement thus resulting in the IMPORT_NUMBER being an invalid identifier. Any help would be much appreciated.
EXECUTE IMMEDIATE
'ALTER TABLE TARGET_TABLE ADD IMPORT_NUMBER NUMBER';
INSERT
INTO
TARGET_TABLE(
existing_col_1,
existing_col_2,
existing_col_3,
IMPORT_NUMBER
)
SELECT
STAGED_TABLE.value1,
STAGED_TABLE.value2,
STAGED_TABLE.value3,
STAGED_TABLE.IMPORT_NUMBER
FROM
STAGED_TABLE
GROUP BY
IMPORT_NUMBER;
UPDATE
PARENT_TABLE
SET
target_table_id =(
SELECT
TARGET_TABLE.id
FROM
TARGET_TABLE
WHERE
TARGET_TABLE.IMPORT_NUMBER = PARENT_TABLE.IMPORT_NUMBER
)
WHERE
PARENT_TABLE.IMPORT_NUMBER IS NOT NULL;
EXECUTE IMMEDIATE 'ALTER TABLE TARGET_TABLE DROP COLUMN IMPORT NUMBER';
If this is a stored procedure, the entire procedure is parsed and validated at the time of create or replace procedure. At the time the procedure is created the column IMPORT_NUMBER does not exist, so the insert and update statements can not be validated.
I would try to find a solution that does not include DDL if possible. Can the column be made a permanent part of the table?
If you must follow this path, the insert and update statements will need to be in strings and passed to execute immediate or DBMS_SQL so that they are parsed and validated at run time, after the column is created.
I'm trying to add new columns to a table then update the table and set the new column with a date format change of the old column.
I have my procedure set out as follows:
begin
alter table [dbo].[mytable]
add New_Field1 varchar(24)
end
......
update [dbo].[SMR06_TARGET]
set New_Field1 = convert(varchar(24),Old_Field1,103)
.....
I have multiple alter table statements at the top of the table and update statements at the bottom for each new column. I think this is a rule with SQL keeping DDL at top and DML at bottom.
Ok so everytime I execute this to create the procedure it fails with incorrect column name New_Field1. I really can't peg down what is causing this. I've tried different variations of BEGIN....END tried commenting out the apprent offending statement, then it runs, then it fails again with the next statement.
I'm reckoning it's something to do with the way the statement(s) are terminated. I'm not sure as haven't done this type of procedure statement before with mixed DDL/DML.
Any hints would be most welcome.
Thanks
Andrew
You need to batch the statement that adds the column separately from the statement that updates it.
BEGIN TRANSACTION
GO
ALTER TABLE [dbo].[mytable]
ADD New_Field1 varchar(24) NULL
GO
UPDATE [dbo].[mytable]
SET New_Field1 = convert(varchar(24),Old_Field1,103)
GO
ALTER TABLE dbo.Batch SET (LOCK_ESCALATION = TABLE)
GO
COMMIT
The entire batch is reviewed by the parser before it starts executing the first line. Adding Old_Field1 is in the same batch as the reference to use Old_Field1. At the time the parser considers the statement containing Old_Field1, the statement to add Old_Field1 has not been executed, so that field does not yet exist.
If you're running in SSMS, include GO between each statement to force multiple batches. If you're running this in another tool that can't use GO, you'll need to submit each statement individually to ensure that they are fully executed before the next step is parsed.
I am trying to create temporary table in stored procedure in Firebird database.
My stored procedure listing:
SET TERM ^ ;
CREATE PROCEDURE initNATIONALHEALTHFUNDS
AS BEGIN
CREATE GLOBAL TEMPORARY TABLE temp_FUNDS
(
NATIONALHEALTHFUNDID Integer NOT NULL,
NAME Varchar(128) NOT NULL,
CODE Integer NOT NULL
)
ON COMMIT PRESERVE ROWS;
commit;
INSERT INTO tempFUNDS (NATIONALHEALTHFUNDID, CODE, NAME) VALUES ( 01 ,01 , 'Some Foundation');
MERGE INTO NATIONALHEALTHFUNDS AS target
USING tempFUNDS AS source
ON target.NATIONALHEALTHFUNDID = source.NATIONALHEALTHFUNDID
WHEN NOT MATCHED THEN
INSERT (NATIONALHEALTHFUNDID, CODE, NAME) VALUES (source.NATIONALHEALTHFUNDID, source.CODE, source.NAME);
drop TABLE tempFUNDS;
END^
SET TERM ; ^
Each time I am trying create this procedure I am getting error:
Engine Code : 335544569
Engine Message :
Dynamic SQL Error
SQL error code = -104
Token unknown - line 7, column 3
CREATE
Total execution time: 0.015s
What I am doing wrong? I'm using Firebird 3.0 RC
Firebird doesn't allow you to use DDL inside stored procedures, so CREATE statements are disallowed in PSQL. As indicated in the answer by lad2025 you can work around this limitation by using EXECUTE STATEMENT.
However, the idea behind a global temporary table is that you create it once, and they continue to exist so they can be used later. The data is only visible to the connection that created the data, and the data is deleted after transaction commit (ON COMMIT DELETE ROWS) or connection close (ON COMMIT PRESERVE ROWS) depending on the type of global temporary table.
From the Firebird 3.0 Language Reference:
Global temporary tables have persistent metadata, but their contents
are transaction-bound (the default) or connection-bound. Every
transaction or connection has its own private instance of a GTT,
isolated from all the others. Instances are only created if and when
the GTT is referenced. They are destroyed when the transaction ends or
on disconnection.
So instead of trying to create the global temporary table inside your stored procedure, create it first, then create your stored procedure that uses the already defined GTT.
From GTT documentation:
CREATE GLOBAL TEMPORARY TABLE
is a regular DDL statement that is processed by the engine the same way as a CREATE TABLE statement is processed. Accordingly, it not
possible to create or drop a GTT within a stored procedure or trigger.
You can use Dynamic-SQL and wrap your code with EXECUTE STATEMENT as workaround:
SET TERM ^ ;
CREATE PROCEDURE initNATIONALHEALTHFUNDS
AS BEGIN
EXECUTE STATEMENT
'CREATE GLOBAL TEMPORARY TABLE temp_FUNDS
(
NATIONALHEALTHFUNDID Integer NOT NULL,
NAME Varchar(128) NOT NULL,
CODE Integer NOT NULL
)
ON COMMIT PRESERVE ROWS;
commit;';
...
END^
Just to elaborate on the other above correct answers, I use temporary tables mostly for performance issues, like when I have a parameterized subset of data that needs to be query against a larger set like:
select * from MAIN_TABLE
where MAIN_TABLE.ID in (select ID from GTT$IDS)
where GTT$IDS is populated with the subset of ID's.
Sometimes, for highly complex procedures, I have to use multiple temp tables, so I create them in the metadata (outside of PSQL statements, of course) like so:
create global temporary table GTT$IDS_1 (INT1 integer, INT2 integer);
create index IDX_GTT$IDS_11 on GTT$IDS_1 (INT1);
create index IDX_GTT$IDS_12 on GTT$IDS_1 (INT2);
create global temporary table GTT$IDS_2
...
create global temporary table GTT$IDS_3
...
Doing this may be simplistic for some advanced SQL'ers out there, but it makes the most sense to me (carry over technique from my dBase/VFP days) and it's super fast compared to a bunch of complex joins.
I never really took the time to learn how to use the 'PLAN' clause (or get it working right), so basically I use this technique to generate the PLAN through code when I get slow queries, if that makes sense.