How to list similar column names with select statement? - netezza

My table has several columns that have similar names, such as year_1, year_2, year_3, etc. Is there an easy way to list them all without typing all of them? I cannot use select *, because I don't want the other columns. Thanks.

You'll have to use dynamic nzplsql in a stored procedure. Stored procedures don't easily return tables, though, but you can have it easily output a select statement that builds from _v_relation_column via a cursor. Capture that in bash and feed it to nzsql to select from a table. Either that or you can just return a reftable.
/* Stored procedure header. */
declare sql varchar;
declare col record;
begin_proc
sql := 'select ';
for col in select * from _v_relation_column where name = 'TABLE_NAME' loop
if col.attname like 'year%'
sql := sql || attname || ',';
end if;
sql := substring(sql,1,length(sql)-1); --To strip the last comma. Could probably be more elegant.
sql := sql || ' from table_name;';
raise notice '%',sql;
end_proc;
/* Stored procedure footer. */

Related

write multipe statments in snowflake

Hi i have one doubt in snowflake how to write multiple update stments using stored procedure.
i have tried like below
create or replace procedure sp_multipleupdate()
returns table()
lANGUAGE sql
as
$$
declare res rsultset(
update TEST.PUBLIC.DEPT set Dname='PM' where deptid=10;
update TEST.PUBLIC.emp set name='veavi' where deptno=20;
update TEST.PUBLIC.loc set locname='del' where id=5;
)
begin
return table(res);
end;
$$;
getting error :
000006 (0A000): Multiple SQL statements in a single API call are not supported; use one API call per statement instead.
Syntax error: unexpected '('. (line 2)
please let me know how to write query to achive this task in snowflake server .
Multiple SQL statements inside the resultset are not supported.
Rather than writing the UPDATE statements like that I would create a more generic procedure and pass arguments to it, so maybe split the above one in 3 procedures since these UPDATE statements are for different tables.
Here is a sample of a generic stored procedure:
create or replace procedure find_invoice_by_id_via_execute_immediate(id varchar)
returns table (id integer, price number(12,2))
language sql
as
declare
select_statement varchar;
res resultset;
begin
select_statement := 'SELECT * FROM invoices WHERE id = ' || id;
res := (execute immediate :select_statement);
return table(res);
end;
You can read more here.

Is there any equivalent in Snowflake for OBJECT_ID() in SQL Server?

The OBJECT_ID function Returns the database object identification number of a schema-scoped object in SQL SERVER.
Could anyone suggest an equivalent function in Snowflake that can be used inside a stored procedure?
I need to migrate the below code to Snowflake:
CREATE PROCEDURE test_procedure
(#Var1 INT )
AS
BEGIN
IF #Var1 = 1
BEGIN
IF OBJECT_ID('db1.Table1') IS NOT NULL
DROP TABLE Table1;
END;
END;
The pattern:
IF OBJECT_ID('db1.Table1') IS NOT NULL
DROP TABLE Table1;
is the old way to check if table exists before trying to drop it.
Currently both SQL Server and Snowflake supports IF EXISTS clause:
DROP TABLE IF EXISTS <table_name>;
db<>fiddle demo
The closest I can think of will be to use a function like:
CREATE OR REPLACE FUNCTION OBJECT_ID(NAME VARCHAR) RETURNS STRING
LANGUAGE SQL
AS
$$
SELECT OBJECT_SCHEMA || '.' || OBJECT_NAME FROM INFORMATION_SCHEMA.OBJECT_PRIVILEGES WHERE
(OBJECT_SCHEMA || '.' || OBJECT_NAME) = NAME
$$;
The snowflake object_privileges view is the closest information_schema view listing all db elements.
However as noted on previous answers, the IF EXISTS on create and drop statements, makes the usage of this function unnecessary

Generating create stored procedure script using SQL syntax only

I am aware it is possible in SQL Server Management Studio to generate a create stored procedure script using the Object Explorer (right click on stored procedure, "Script stored procedure as...", Create To)
Is it possible to generate a create script string using SQL syntax only?
declare #createSPstring varchar(max)
/*
insert code to generate the create stored procedure string and put it into #createSPString...
*/
select #createSPstring
You can get the full text from the view:
sys.sql_modules
However your selectmight not get the full code, since a nvarchar(maX) is cut of in SSMS.
One option is to use print to print each row from the definition. Here I split the definition on char(13) to get each row and use a cursor (yes i know) to print each row.
DECLARE #createSPstring VARCHAR(MAX)
-- get definition of procedure "test"
SELECT #createSPstring = definition
FROM sys.sql_modules
WHERE object_id = OBJECT_ID('test')
-- Declare cursor - split definition on line break
DECLARE rows CURSOR FOR
SELECT [value] row
FROM STRING_SPLIT(#createSPstring, CHAR(13))
DECLARE #row NVARCHAR(MAX)
OPEN rows
FETCH NEXT FROM rows INTO #row
WHILE ##fetch_status = 0
BEGIN
--Print each row
PRINT REPLACE(#row,'char(10)','')
FETCH NEXT FROM rows INTO #row
END
select cast (definition as xml ) from sys.sql_modules
WHERE object_id = OBJECT_ID('test')
click on the result, you can get the sp with the same format you created. only problem is if your proc using > or < operators; it will be replaced with > <
you require to replace manually using ctrl+h

Snowflake conditional code: adding new column(idempotent script)

Let's assume we have a table that contains data as below:
CREATE TABLE tab(i INT PRIMARY KEY);
INSERT INTO tab(i) VALUES(1),(2),(3);
SELECT * FROM tab;
Now my goal is to create SQL script that will add a new column to existing table:
ALTER TABLE IF EXISTS tab ADD COLUMN col VARCHAR(10);
Everything works as intended. Except the fact I would like to be able to run script multiple times but the effect should take place only once(idempotence).
If I try to run it again I will get:
SQL compilation error: column COL already exists
Normally I would use one of these approaches:
a) Using control structure IF to check metadata tables before executing query:
-- (T-SQL)
IF NOT EXISTS(SELECT * FROM INFORMATION_SCHEMA.COLUMNS
WHERE TABLE_NAME='TAB' AND COLUMN_NAME = 'COL')
BEGIN
ALTER TABLE tab ADD col VARCHAR(10);
END;
db<>fiddle demo
I have not found IF statement in Snowflake's documentation.
b) SQL dialect that supports IF NOT EXISTS syntax:
-- PostgreSQL
ALTER TABLE IF EXISTS tab ADD COLUMN IF NOT EXISTS col VARCHAR(10);
db<>fiddle demo
Most of Snowflake SQL commands contain IF EXISTS/OR REPLACE clauses which means it was written in a way to allow running scripts multiple times.
I was considering using code like:
CREATE OR REPLACE TABLE tab
AS
SELECT i, CAST(NULL AS VARCHAR(10)) AS col
FROM tab;
This approach on other hand causes unnecessary table creation and does not preserve metadata(like primary key).
Is there a way to achieve similar effect on Snowflake? Preferably by using conditional code(add column is an example).
You can use something like this. It will report the failure to add the column if it already exists, but it will handle the error so it won't interfere with the execution of a sql script:
create or replace procedure SafeAddColumn(tableName string, columnName string, columnType string)
returns string
language JavaScript
as
$$
var sql_command = "ALTER TABLE IF EXISTS " + TABLENAME + " ADD COLUMN " + COLUMNNAME + " " + COLUMNTYPE + ";";
var strOut;
try {
var stmt = snowflake.createStatement( {sqlText: sql_command} );
var resultSet = stmt.execute();
while (resultSet.next()) {
strOut = resultSet.getColumnValue(1);
}
}
catch (err) {
strOut = "Failed: " + err; // Return a success/error indicator.
}
return strOut;
$$;
CREATE OR REPLACE TABLE tab(i INT PRIMARY KEY);
INSERT INTO tab(i) VALUES(1),(2),(3);
SELECT * FROM tab;
call SafeAddColumn('tab', 'col', 'varchar(10)');
select * from tab;
call SafeAddColumn('tab', 'col', 'varchar(10)');
It is possible to write conditional code using Snowflake Scripting.
Working with Branching Constructs
Snowflake Scripting supports the following branching constructs:
IF-THEN-ELSEIF-ELSE
CASE
Setup:
CREATE OR REPLACE TABLE PUBLIC.tab(i INT PRIMARY KEY);
INSERT INTO tab(i) VALUES(1),(2);
SELECT * FROM tab;
-- i
-- 1
-- 2
Code that can be rerun multiple times(subsequent runs will take no effect):
-- Snowsight
BEGIN
IF (NOT EXISTS(SELECT *
FROM INFORMATION_SCHEMA.COLUMNS
WHERE TABLE_NAME = 'TAB'
AND TABLE_SCHEMA = 'PUBLIC'
AND COLUMN_NAME = 'COL')) THEN
ALTER TABLE IF EXISTS tab ADD COLUMN col VARCHAR(10);
END IF;
END;
EXECUTE IMMEDIATE is required is run using "classic web interface":
EXECUTE IMMEDIATE $$
BEGIN
IF (NOT EXISTS(SELECT *
FROM INFORMATION_SCHEMA.COLUMNS
WHERE TABLE_NAME = 'TAB'
AND TABLE_SCHEMA = 'PUBLIC'
AND COLUMN_NAME = 'COL')) THEN
ALTER TABLE IF EXISTS tab ADD COLUMN col VARCHAR(10);
END IF;
END;
$$
After:
SELECT * FROM tab;
-- i col
-- 1 NULL
-- 2 NULL
Although Snowflake has implemented a pretty rich mix of DDL and DML for their SQL implementation, when it comes to procedural code they seem to be relying on JavaScript, at least at this point. But you should be able to accomplish your idempotent ALTER script through a JavaScript stored procedure.
I'm afraid I lack the JavaScript skills to provide you with a working sample myself at this point. The organization I'm with recently adopted Snowflake, though, so I'll share some of my research.
Here's a recent blog post on just this question:
Snowflake Control Structures – IF, DO, WHILE, FOR
Snowflake's overview documentation regarding stored procedures:
Stored Procedures
On the page above, what is currently the third link down contains extensive sample code.
Working With Stored Procedures
Building on Lukasz answer, to include database in condition you can use:
execute immediate $$
BEGIN
IF (
NOT EXISTS(
SELECT *
FROM "INFORMATION_SCHEMA"."COLUMNS"
WHERE
"TABLE_CATALOG" = 'DB_NAME'
AND "TABLE_SCHEMA" = 'SCHEMA_NAME'
AND "TABLE_NAME" = 'TABLE_NAME'
AND "COLUMN_NAME" = 'col_name'
)
) THEN
ALTER TABLE IF EXISTS "DB_NAME"."SCHEMA_NAME"."TABLE_NAME"
ADD COLUMN "col_name" VARCHAR NULL;
END IF;
END;
$$;

How to compare the count of each table in two different databases?

I have two databases: db1 and db2 (db2 was completely empty). I was copying all the db1 to db2 but the progress was interrupted and I need to know which tables are still left to copy. How can I compare the count of each table in these two databases to know which tables I still have to transfer?
Basically, you need to loop through the data dictionary and generate some dynamic SQL which executes a count for each table.
I have assumed you're only transferring one schema. If that's not true, or you're not connecting as the target schema, you'll need to use ALL_TABLES instead of USER_TABLES, and include the OWNER column in the driving query and the dynamic query too.
declare
n pls_integer;
stmt varchar2(32767);
begin
for r in ( select table_name from user_tables order by table_name ) loop
stmt := 'select count(*) from ' || r.table_name;
-- uncomment the next line to debug errors
-- dbms_output.put_line(stmt);
execute immediate stmt into n;
-- you may wish to only display empty tables
-- if n = 0 then
dbms_output.put_line(r.table_name || ' = ' || lpad(n, 10));
-- end if;
end loop;
end;
One would hope that your data copying process was clever enough to commit only completed tables. If so you only need to run this on DB2. Otherwise on both.

Resources