run a query saved in a row value - sql

The following select statement gives me three cols and one row.
QueryA
SELECT
-- some calculations and manipulations
x, y, queryB
.....
FROM INFORMATION_SCHEMA.COLUMNS c
WHERE TABLE_SCHEMA = UPPER($sch_name)
AND TABLE_NAME = UPPER($tab_name);
One of the returned columns has a query stored. Output
x, y, queryB
aa bb select * from stg.new
How can I extend the QueryA such that I select and run the query B and the final output returned is that of queryB not queryA?

Here is a sample of reading from one table to get the SQL, executing the SQL on a different table, and then returning the result of that second statement:
create or replace transient table T1 as
select 'This is the final result.' as THE_RESULT
;
create or replace transient table SQL_TO_RUN as
select 'select * from T1' SQL_STATEMENT
;
create or replace procedure myprocedure()
returns table()
language sql
execute as caller
as
$$
declare
rs resultset default (select SQL_STATEMENT from SQL_TO_RUN);
c cursor for rs;
sqlStatement string;
rsFinal resultset;
begin
for rowContents in c do
sqlStatement := rowContents.SQL_STATEMENT;
end for;
rsFinal := (execute immediate :sqlStatement);
return table(rsFinal);
end;
$$
;
call myprocedure();

Related

Trying to query a redshift within SELECT statement

Current table1:
col1
-------------
schema.table1
schema.table2
schema.table3
Desired table1:
col1 col2
------------------------------------------------------------
schema.table1 value of (select count(*) from schema.table1)
schema.table2 value of (select count(*) from schema.table1)
schema.table3 value of (select count(*) from schema.table1)
It is not working, I tried using function too, but function doesn't allow to use 'FROM'
select col1, (select count(*) from col1)
from table1
I am trying to create this query in redshift. Can anyone please help me out?
To perform this task you will need a stored procedure AND a defined cursor. The stored procedure allows for looping and the cursor provides the ability to execute a newly created statement (dynamic querying).
For example:
Create the starting materials, 3 tables and a table that references these tables.
create table foo as (select 1 as A);
create table goo as (select 2 as A);
create table hoo as (select 3 as A);
create table tabs as (select 'foo' as tab union all select 'goo' union all select 'hoo');
Next define the stored procedure the will create the dynamic SQL
CREATE OR REPLACE procedure count_tabs(curs1 INOUT refcursor)
AS
$$
DECLARE
row record;
statement varchar := '';
union_needed BOOL := false;
BEGIN
for row in select tab from tabs LOOP
IF union_needed THEN
statement := statement || ' UNION ALL ';
END IF;
statement := statement || 'select \'' || row.tab || '\' as table_name, count(*) as table_count from ' || row.tab ;
union_needed := true;
END LOOP;
RAISE NOTICE 'sql to execute: %',statement;
open curs1 for execute statement;
END;
$$ LANGUAGE plpgsql;
Lastly we need to call the procedure and execute the cursor
call count_tabs('mycursor');
fetch 1000 from mycursor;
A few notes on this:
This assumes you want the results as output on your bench. If you want to create a table with the results this is doable in the same structure
Since the FROM clause value(s) is unknown at compile time this needs to be done in 2 steps - create the query and then execute the query.
I believe you can have the procedure walk this same cursor itself but doing this is exceptionally slow

Use of variable in result_scan() snowflake stored procedure

I am trying to store the result of a last_query_id() as a variable and then later on in the stored procedure I am trying to use it in a table(result_scan()). I am coming across a number of errors when doing this, however I'm sure it's my level of knowledge of how snowflake works that s the problem. My code currently looks like this:
Declare query_id as varchar;
...
a number of queries;
...
query_id := (select last_query_id());
...
Some more queries
...
let c1 CURSOR for select * from table(result_scan(:query_id));
This is giving me an error saying result_scan() requires a string. I have tried using CAST to convert it to a string however this is not working either.
Thanks!
The assignment is possible:
BEGIN
LET query_id varchar;
SELECT 1 AS c;
query_id := (select last_query_id());
RETURN :query_id;
END;
The RESULT_SCAN function requires string literal so the following will not work:
DECLARE
query_id varchar;
r INT;
BEGIN
SELECT 1 AS c;
query_id := (select last_query_id());
LET c1 CURSOR for select * from table(result_scan(?));
OPEN C1 USING(:query_id);
FETCH c1 INTO r;
RETURN r;
END;
A workaround could be usage of EXECUTE IMMEDIATE and RESULTSET:
DECLARE
rs RESULTSET;
query_id VARCHAR;
BEGIN
SELECT 1 AS col1, 'a' AS col2;
query_id := (SELECT last_query_id());
let sql VARCHAR := 'select * from table(result_scan(''' || query_id || '''))';
rs := (EXECUTE IMMEDIATE :sql);
RETURN TABLE(rs);
END;
Output:
the syntax for assigning the result of a SELECT to a variable is
SELECT COL1 ... INTO variable
It's documented here

Debugging a PL/pgSQL function

I am trying to get this PL/pgSQL function to work :
CREATE OR REPLACE FUNCTION loopcolumns2(tableName TEXT, pourcentage real)
RETURNS void AS $$
DECLARE
_name text;
missing_percentage real;
BEGIN
FOR _name IN SELECT column_name from information_schema.columns where table_name=tableName LOOP
SELECT 100 - (count(_name) * 100) / count(*) INTO missing_percentage FROM tableName;
IF (missing_percentage > pourcentage)
THEN ALTER TABLE tableName DROP COLUMN _name;
END IF;
END LOOP;
END; $$ LANGUAGE plpgsql;
The goal of the function is to loop through all the columns of a table, a delete the columns where the percentage of missing values is bigger than an input percentage.
I get the following error :
SELECT 100 - (count( $1 ) * 100) / count(*) FROM $2
^
CONTEXT: SQL statement in PL/PgSQL function "loopcolumns2" near line 6
You're trying to SELECT from text stored by tableName vaiable. You cannot do that because Postgres think you want him to select from table named tableName, but probably such table doesn't exist.
You nead to create dynamic query in string and use EXECUTE ... INTO ... statement. Like this:
DECLARE query TEXT;
...
query := 'SELECT 100 - (count(' || _name::TEXT || ') * 100) / count(*) FROM '
|| tableName::TEXT;
EXECUTE query INTO percentage ;
...

Dynamic Sql: Create array from records using array of column names

I am pulling all of the column_names (cname1) from a crosstab table that I made. There are thousands of these column names so I combined them into an array. I then want to use dynamic sql (or whatever works) to use those column_names to make an array based off of the records of that same crosstab table. I keep getting the error:
ERROR: missing "LOOP" at end of SQL expression
.
CREATE OR REPLACE FUNCTION mffcu.test_ty_hey()
RETURNS setof record
LANGUAGE plpgsql
AS $function$
Declare
cname1 text;
Begin
for cname1 in select array_agg(column_name) as useme
from(
select column_name::text
FROM INFORMATION_SCHEMA.COLUMNS
WHERE TABLE_NAME = 'crosstab_183'
and ordinal_position != 1
) as fin
join mffcu.crosstab_183 a on fin.id = a.id;
loop
sql2 ='select distinct array['|| columnname ||'] from mffcu.crosstab_183';
execute sql2;
end loop;
END;
$function$
I cannot for the life of me figure out why I'm getting this error.
for cname1 in select array_agg(column_name) as useme
from(
select column_name::text
FROM INFORMATION_SCHEMA.COLUMNS
WHERE TABLE_NAME = 'crosstab_183'
and ordinal_position != 1
) as fin
join mffcu.crosstab_183 a on fin.id = a.id; --here should not be semicolon!
loop

MySQL Backup Table if it Exists

I am trying to write a script that will copy all the data in table a to table b if table a exists. Table b is the exact same structure as table a would be if it exists, though it may not. I am able to copy using the following statement: INSERT INTO 'B' SELECT * FROM 'A', but I don't know where to use IF EXISTS, or if I even can to determine if I an perform the insertion. I am trying to do this in SQL only as it will be run through as a .sql script from the command line.
MySQL only:
DROP PROCEDURE IF EXISTS myupdate;
DELIMITER //
CREATE PROCEDURE myupdate ()
BEGIN
DECLARE found VARCHAR(64);
SET found = (SELECT TABLE_NAME FROM INFORMATION_SCHEMA.TABLES WHERE TABLE_SCHEMA = Database() AND TABLE_NAME = 'A');
IF found = 'types' THEN
INSERT INTO B SELECT * FROM A;
SELECT 'A into B';
ELSE
SELECT 'A not found';
END IF;
END;//
DELIMITER ;
CALL myupdate();
DROP PROCEDURE myupdate;
Expand to you're liking comparing the column definition in INFORMATION_SCHEMA.COLUMNS for A & B if you need finer control.
I have accepted Wrikken's answer but am using this as my final code. I need to reuse the procedure he provided for multiple tables, so I modified it slightly. It makes the assumption that the backup table has already been created.
DROP PROCEDURE IF EXISTS tableUpdate;
DELIMITER //
CREATE PROCEDURE tableUpdate(name VARCHAR(32))
BEGIN
DECLARE cnt tinyint(1);
DECLARE btable VARCHAR(36);
SET btable = CONCAT(name,'BAK');
SET cnt = (SELECT COUNT(TABLE_NAME) FROM INFORMATION_SCHEMA.TABLES WHERE TABLE_SCHEMA = 'TAA' AND TABLE_NAME = name);
IF cnt > 0 THEN
SET #q:= CONCAT('INSERT INTO ',CONCAT(btable,CONCAT(' SELECT * FROM ',name)));
PREPARE stmt FROM #q;
EXECUTE stmt;
COMMIT;
ELSE
SELECT 'No update necessary.';
END IF;
END;//
DELIMITER ;
CALL tableUpdate('A');
DROP PROCEDURE tableUpdate;
You can do so by performing the following:
select count(*) from my_tables where table_name='b';
If count>0 then
update b...;
else
create table b;
insert into my_tables(table_name) values('b');
insert into b...;
end if;