How to drop an existing index in Firebird? - sql

What a script should I create to check is this index exist? Because I want this index will be dropped if it has been created yet and then create the index again
CREATE INDEX IF NOT EXISTS IDX_TABLE ON TABLE (ID, DATE)
I need only one script to make it automatically. I have not found an alternative for with drop_existing = ON as in MSSQL.

Unfortunately, Firebird does not allow you to conditionally drop or create an index in its SQL dialect. If you are executing per statement, you could catch errors and ignore the relevant error codes. Alternatively, you could use an execute block, something like:
execute block as
begin
if (exists(select * from rdb$indices where rdb$index_name = 'IDX_TABLE')) then
execute statement 'drop index IDX_TABLE';
end
The use of execute statement is necessary, because PSQL (the Firebird procedural language) does not support DDL statements directly.
If instead you want to conditionally create an index, you can use:
execute block as
begin
if (not exists(select * from rdb$indices where rdb$index_name = 'IDX_TABLE')) then
execute statement 'create index IDX_TABLE on table (id, name)';
end
The RDB$INDICES table is a Firebird system table.

Related

Is it possible to pass variable tables through procedures in SQL DEV?

set serveroutput on;
CREATE OR REPLACE PROCEDURE test_migrate
(
--v_into_table dba_tables.schema#dbprd%TYPE,
--v_from_table dba_tables.table#dbprd%TYPE,
v_gid IN NUMBER
)
IS
BEGIN
select * INTO fx.T_RX_TXN_PLAN
FROM fx.T_RX_TXN_PLAN#dbprd
WHERE gid = v_gid;
--and schema = v_into_table
--and table = v_from_table;
COMMIT;
END;
I thought that SELECT * INTO would create a table in the new database from #dbprd. However, the primary issue is just being able to set these as variables and the goal is to EXEC(INTO_Table,FROM_Table,V_GID) to run the above code.
Error(9,19): PLS-00201: identifier 'fx.T_RX_TXN_PLAN' must be
declared  Error(10,5): PL/SQL: ORA-00904: : invalid identifier
If your goal is to copy data from table in "another" database into a table that resides in "this" database (regarding database link you used), then it it INSERT INTO, not SELECT INTO.
For example:
CREATE OR REPLACE PROCEDURE test_migrate (v_gid in number)
IS
BEGIN
insert into fx.t_rx_txn_plan (col1, col2, ..., coln)
select col1, col2, ..., coln
from fx.t_rx_txn_plan#dbprod
where gid = v_gid;
END;
Last sentence you wrote looks like you'd want to make it dynamic, i.e. pass table names and v_gid (whatever that might be; looks like all tables that should be involved into this process have it). That isn't a simple task.
If you plan to use insert into select * from, that's OK but not for production system. What if someone alters a table and adds (or drops) a column or two? Your procedure will automatically fail. Correct way to do it is to enumerate all columns involved, but that requires fetching data from user_tab_columns (or all_ or dba_ version of the same), which complicates it even more.
Therefore, if you want to move data from here to there, why don't you do it using Data Pump Export & Import? Those utilities are designed for such a purpose, and will do the job better than your procedure. At least, I think so.
This way you should be returning a row. If so, add an OUT type parameter to the procedure with
CREATE OR REPLACE PROCEDURE test_migrate(
--v_into_table dba_tables.schema#dbprd%TYPE,
--v_from_table dba_tables.table#dbprd%TYPE,
i_gid IN NUMBER,
o_RX_TXN_PLAN OUT fx.T_RX_TXN_PLAN#dbprd%rowtype
) IS
BEGIN
SELECT *
INTO RT_RX_TXN_PLAN
FROM fx.T_RX_TXN_PLAN#dbprd
WHERE id = v_gid;
--and schema = v_into_table
--and table = v_from_table;
END;
and call the procedure such as
declare
v_rx_txn_plan fx.T_RX_TXN_PLAN#dbprd%rowtype;
v_gid number:=5345;
begin
test_migrate(v_gid => v_gid, rt_rx_txn_plan => v_rx_txn_plan);
dbms_output.put_line(v_rx_txn_plan.col1);
dbms_output.put_line(v_rx_txn_plan.col2);
end;
to print out the returning values for some columns of the table. to be able to create a new table from this, not SELECT * INTO ... syntax, but
CREATE TABLE T_RX_TXN_PLAN AS
SELECT *
INTO RT_RX_TXN_PLAN
FROM fx.T_RX_TXN_PLAN#dbprd
WHERE ...
is used.
But neither of the cases to issue a COMMIT since there's no DML exists within them.
To create a table you must use the CREATE TABLE statement, and to use any DDL statement in PL/SQL you have to use EXECUTE IMMEDIATE:
CREATE OR REPLACE PROCEDURE test_migrate
(
v_gid IN NUMBER
)
IS
BEGIN
EXECUTE IMMEDIATE 'CREATE TABLE FX.T_RX_TXN_PLAN AS
SELECT *
FROM fx.T_RX_TXN_PLAN#dbprd
WHERE gid = :GID'
USING IN v_gid;
END;

Executing DDL in compound SQL using DashDB (DB2)

I need to execute a DDL command (CREATE TABLE) with other SQL commands. See the code snippet below:
CREATE TABLE test AS
(
SELECT duration AS NUM1
FROM event
WHERE duration IS NOT NULL
) WITH NO DATA;
INSERT INTO test (
SELECT duration AS NUM1
FROM event
WHERE event_duration_tech IS NOT NULL
);
I am creating a table, then populating it.
If I send this code via JDBC, it does not work due to a statement terminator (;) error.
If I wrap it with BEGIN and END to create a compound SQL block, it does not work because DB2 does not allow DDL commands on compound SQL blocks.
The thing is, I need to execute both commands in one shot. Any ideas?
You need to use dynamic SQL to execute some DDL statements:
EXECUTE IMMEDIATE 'CREATE TABLE test AS (SELECT...'

Only ALTER a TRIGGER if is exists

I am implementing an auditing system on my database. It uses triggers on each table to log changes.
I need to make modifications to these triggers and so am producing ALTER scripts for each one.
What I'd like to do is only have these triggers be altered if they exist, ideally like so:
IF EXISTS (SELECT * FROM sysobjects WHERE type = 'TR' AND name = 'MyTable_Audit_Update')
BEGIN
ALTER TRIGGER [dbo].[MyTable_Audit_Update] ON [dbo].[MyTable]
AFTER Update
...
END
However when I do this I get an error saying "Invalid syntax near keyword TRIGGER"
The reason that these triggers may not exist is that auditing can be enabled/disabled on tables which the end user can specify. This involves either creating or dropping the triggers. I am unable to make the changes to the triggers upon creation as they are dynamically created and so I must still provide a way altering the triggers should they exist.
The alter statement has to be the first in the batch. So for sql server it would be:
IF EXISTS (SELECT * FROM sysobjects WHERE type = 'TR' AND name = 'MyTable_Audit_Update')
BEGIN
EXEC('ALTER TRIGGER [dbo].[MyTable_Audit_Update] ON [dbo].[MyTable]
AFTER Update
...')
END
This might be a similar issue to one that I found with Sybase years ago, where I found that when trying to execute a create table statement conditionally, the DDL is executed prior to assessing the conditional statement. The only workaround was to use execute immediate.
Unlike CREATE TRIGGER, I failed to find a reference that explicitly states that
CREATE TRIGGER must be the first statement in the batch
but it seems that this restriction applies to ALTER TABLE too.
The simple way to do this would be to DROP the TRIGGER and re-create it:
IF EXISTS (SELECT * FROM sysobjects WHERE type = 'TR' AND name = 'MyTable_Audit_Update')
DROP TRIGGER MyTable_Audit_Update
GO
CREATE TRIGGER [dbo].[MyTable_Audit_Update] ON [dbo].[MyTable]
AFTER Update
...
END

Create Database and Table Conditionally

I'm trying to write a small script to create a database if it doesn't exist, and create a table for that database if the table doesn't exist. What I have is this:
IF (db_id('db') is null) BEGIN
print 'Must create the database!';
CREATE DATABASE db;
END
USE db;
IF (object_id('test_table', 'U') is null) BEGIN
print 'Must create the table!';
CREATE TABLE test_table (
id int
);
END
I'm getting a strange error with this:
Database 'db' does not exist. Make sure that the name is entered correctly.
I'm guessing that it's parsing the script before running it and finding that 'db' doesn't exist, so it can't use it.
There must be a solution to this. Any help is appreciated.
SOLVED!
I realised 5 minutes after posting that the GO keyword solves the problem. Here is the fixed code:
IF (db_id('db') is null) BEGIN
print 'Must create the database!'
CREATE DATABASE db;
END
GO
USE db
IF (object_id('test_table', 'U') is null) BEGIN
print 'Must create the table!';
CREATE TABLE test_table (
id int
);
END
Sorry for wasting everyone's time.
SQL statements are parsed as one batch unless you break them apart. In SQL Server, you can use GO to do this. In both MySQL and SQL Server, you can use BEGIN and END.
If you want to commit the separate blocks to the database in different instances you can use BEGIN TRANS / COMMIT TRANS and START TRANSACTION / COMMIT for SQL Server and MySQL, respectively.
Something along the lines of Check if table exists in SQL Server would probably work (With a slight change)
IF (NOT EXISTS (SELECT *
FROM INFORMATION_SCHEMA.TABLES
WHERE TABLE_SCHEMA = 'TheSchema'
AND TABLE_NAME = 'TheTable'))
BEGIN
--Do Stuff
END
I might suggest using the built-in SQL syntax -
CREATE DATABASE name IF NOT EXISTS;
And subsequently
CREATE TABLE name(definition) IF NOT EXISTS;

SQL: Why does a CREATE TRIGGER need to be preceded by GO

When making a SQL script to create a trigger on a table, I wanted to check that the trigger doesn't already exist before I create it. Otherwise the script cannot be run multiple times.
So I added a statement to first check whether the trigger exists. After adding that statement, the CREATE TRIGGER statement no longer works.
IF NOT EXISTS (SELECT name FROM sysobjects
WHERE name = 'tr_MyTable1_INSERT' AND type = 'TR')
BEGIN
CREATE TRIGGER tr_MyTable1_INSERT
ON MyTable1
AFTER INSERT
AS
BEGIN
...
END
END
GO
This gives:
Msg 156, Level 15, State 1, Line 5
Incorrect syntax near the keyword 'TRIGGER'.
The solution would be to drop the existing trigger and then create the new one:
IF EXISTS (SELECT name FROM sysobjects
WHERE name = 'tr_MyTable1_INSERT' AND type = 'TR')
DROP TRIGGER tr_MyTable1_INSERT
GO
CREATE TRIGGER tr_MyTable1_INSERT
ON MyTable1
AFTER INSERT
AS
BEGIN
...
END
GO
My question is: why is the first example failing? What is so wrong with checking the trigger exists?
Certain statements need to be the first in a batch (as in, group of statements separated by GO ).
Quote:
CREATE DEFAULT, CREATE FUNCTION, CREATE PROCEDURE, CREATE RULE, CREATE SCHEMA, CREATE TRIGGER, and CREATE VIEW statements cannot be combined with other statements in a batch. The CREATE statement must start the batch. All other statements that follow in that batch will be interpreted as part of the definition of the first CREATE statement.
It's simply one of the rules for SQL Server batches (see):
http://msdn.microsoft.com/en-us/library/ms175502.aspx
Otherwise you could change an object, say a table, and then refer to the change in the same batch, before the change was actually made.
Schema changes should always be seperate batch calls...I am guessing they do it to gaurantee your SELECT will succeed, if you modify schema in the same batch they may not be able to gaurantee that. Just a guess...