SQL query with dynamic columns and dynamic data - sql

My query.
INSERT INTO TARGET_TABLE (SELECT DATA FROM TABLE_DATA WHERE TYPE = 'HEADER') VALUES
(SELECT DATA FROM TABLE_DATA WHERE TYPE = 'ITEMS')
In this query I am trying to insert data into TARGET_TABLE.
TABLE_DATA will have data in the below format
SELECT DATA FROM TABLE_DATA WHERE TYPE = 'HEADER'
Result COUNTRY,ID,NAME
SELECT DATA FROM TABLE_DATA WHERE TYPE = 'ITEMS'
Result 'IN','123','xyz'
So I am expecting the below
INSERT INTO TARGET_TABLE (COUNTRY,ID,NAME) VALUES ('IN','123','xyz')
Any thoughts on this?

select case is what you need
INSERT INTO TARGET_TABLE
(SELECT case when type='HEADER' then COL1 else 'aa' END
,case when type='HEADER' then COL2 else 'bb' END
,case when type='HEADER' then COL3 else 'cc' END
,case when type='HEADER' then COL4 else 'dd' END
FROM TABLE_DATA )

You need to use a dynamic query like the following:
BEGIN
EXECUTE IMMEDIATE
'INSERT INTO TARGET_TABLE ( '
|| SELECT DATA FROM TABLE_DATA WHERE TYPE = 'HEADER'
|| ') VALUES ('
|| SELECT DATA FROM TABLE_DATA WHERE TYPE = 'ITEMS'
|| ')';
END;
/
Cheers!!

Related

Apex select list vales as column names

I have a select list (:P31_LIST) with static values (column1, column2, column3) and a table with the same columns names.
How to select and insert data into a column of the table selected by the user, using the select list? How can I use the value of the select list as column table name ? I tried this query but no data was inserted or selected:
-- Select
select column1, :P31_LIST,
---Tried also this
case when :P31_LIST= 'column1' then (select column1 from my_table) END as test,
from my_table
-- Insert
Insert into my_table(:P31_LIST, column2)
SELECT A,
B
FROM source_table
my_table looks like this:
column1 | column2 | column3
1 | 2 | 3
For a SELECT you can either, use a CASE expression and whitelist the column names:
select column1,
CASE :P31_LIST
WHEN 'column1' THEN column1
WHEN 'column2' THEN column2
WHEN 'column3' THEN column3
ELSE NULL
END AS value
from my_table
Or, using dynamic SQL in a PL/SQL block:
DECLARE
v_sql CLOB := 'select column1, ' || :P31_LIST || ' AS value from my_table';
BEGIN
FOR r IN v_sql LOOP
DBMS_OUTPUT.PUT_LINE( r.column1 || ', ' || r.value);
END LOOP;
END;
/
For an INSERT, use PL/SQL:
BEGIN
EXECUTE IMMEDIATE 'INSERT INTO my_table(' || :P31_LIST || ', column2) SELECT A, B FROM source_table';
END;
/
or whitelist the columns:
BEGIN
IF :P31_LIST = 'column1' THEN
INSERT INTO my_table(column1, column2) SELECT A, B FROM source_table;
ELSIF :P31_LIST = 'column3' THEN
INSERT INTO my_table(column3, column2) SELECT A, B FROM source_table;
END IF;
END;
/

Vertica. Count of Null and Not-Null of all columns of a Table

How can we get null and non-null counts of all columns of a Table in Vertica? Table can have n number of columns and for each column we need to get count of nulls and non-nulls values of that table.
For Example.
Below Table has two columns
column1 Column2
1 abc
pqr
3
asd
5
If its a specific column then we can check like
SELECT COUNT(*) FROM table where column1 is null;
SELECT COUNT(*) FROM table where column1 is not null;
Same query for column2
I checked system tables like projection_storage and others but I cant figure out a generic query which gives details by hard coding only TABLE NAME in the query.
Hello #user2452689: Here is a dynamically generated VSQL statement which meets your requirement of counting nulls & not nulls in N columns. Notice that this writes a temporary SQL file out to your working directory, and then execute it via the \i command. You only need to change the first two variables per table. Hope this helps - good luck! :-D
--CHANGE SCHEMA AND TABLE PARAMETERS ONLY:
\set table_schema '\'public\''
\set table_name '\'dim_promotion\''
---------
\o temp_sql_file
\pset tuples_only
select e'select \'' || :table_schema || e'\.' || :table_name || e'\' as table_source' as txt
union all
select * from (
select
', sum(case when ' || column_name || ' is not null then 1 else 0 end) as ' || column_name || '_NOT_NULL
, sum(case when ' || column_name || ' is null then 1 else 0 end) as ' || column_name || '_NULL' as txt
from columns
where table_schema = :table_schema
and table_name = :table_name
order by ordinal_position
) x
union all
select ' from ' || :table_schema || e'.' || :table_name || ';' as txt ;
\o
\pset tuples_only
\i temp_sql_file
You can use:
select count(*) as cnt,
count(column1) as cnt_column1,
count(column2) as cnt_column2
from t;
count() with a column name or expression counts the number of non-NULL values in the column/expression.
(Obviously, the number of NULL values is cnt - cnt_columnX.)
select column1_not_null
,column2_not_null
,column3_not_null
,cnt - column1_not_null as column1_null
,cnt - column2_not_null as column2_null
,cnt - column3_not_null as column3_null
from (select count(*) as cnt
,count (column1) as column1_not_null
,count (column2) as column2_not_null
,count (column3) as column3_not_null
from mytable
) t

Oracle Select which produce Insert into

I have query like
SELECT 'Insert INTO FOO(ID,NAME) values ('||id|| ',' ||NAME||');' as query
FROM FOO
This is just an example as I can't provide real query example. So, DON'T pay attention how it works just believe that it works as it should.
Question:
If NULL values exists in selected rows it just produces '' in INSERT INTO query. So I am getting an error while trying to execute insert query. How can I force SELECT query to return NULL instead of ''?
Example:
I have
Insert INTO FOO(ID,NAME) values (12,);
I want to have
Insert INTO FOO(ID,NAME) values (12,NULL);
Try using CASE EXPRESSION :
SELECT 'Insert INTO FOO(ID,NAME)
values ('||CASE WHEN id = '''' then 'NULL' else id end||','|| CASE WHEN NAME = '''' THEN null else NAME end || ');' as query
FROM FOO
I am guessing that NAME is a string, so it should be surrounded by single quotes. So, this might be the query you want:
SELECT 'Insert INTO FOO(ID,NAME) values (' || id || ', ''' || NAME ||''');' as query
FROM FOO;
If the issue is id, then you can do:
SELECT 'Insert INTO FOO(ID,NAME) values (' || (CASE WHEN id IS NULL THEN 'NULL' ELSE CAST(id AS VARCHAR2(255)) END) ||
', ''' || NAME ||''');' as query
FROM FOO;

PostgreSQL: Get values of a register as multiple rows

Using PostgreSQL 9.3, I'm creating a Jasper reports template to make a pdf report. I want to create reports of different tables, with multiple columns, all with the same template. A solution could be to get values of register as pairs of column name and value per id.
By example, if I had a table like:
id | Column1 | Column2 | Column3
-------------------------------------------------
1 | Register1C1 | Register1C2 | Register1C3
I would like to get the register as:
Id | ColumnName | Value
-----------------------------
1 | Column1 | Register1C1
1 | Column2 | Register1C2
1 | Column3 | Register1C3
The data type of value columns can vary!
Is it possible? How can I do this?
If all your columns share the same data type and order of rows does not have to be enforced:
SELECT t.id, v.*
FROM tbl t, LATERAL (
VALUES
('col1', col1)
, ('col2', col2)
, ('col3', col3)
-- etc.
) v(col, val);
About LATERAL (requires Postgres 9.3 or later):
What is the difference between LATERAL and a subquery in PostgreSQL?
Combining it with a VALUES expression:
Crosstab transpose query request
SELECT DISTINCT on multiple columns
For varying data types, the common denominator would be text, since every type can be cast to text. Plus, order enforced:
SELECT t.id, v.col, v.val
FROM tbl t, LATERAL (
VALUES
(1, 'col1', col1::text)
, (2, 'col2', col2::text)
, (3, 'col3', col3::text)
-- etc.
) v(rank, col, val)
ORDER BY t.id, v.rank;
In Postgres 9.4 or later use the new unnest() for multiple arrays:
SELECT t.id, v.*
FROM tbl t, unnest('{col1,col2,col3}'::text[]
, ARRAY[col1,col2,col3]) v(col, val);
-- , ARRAY[col1::text,col2::text,col3::text]) v(col, val);
The commented alternative for varying data types.
Full automation for Postgres 9.4:
The query above is convenient to automate for a dynamic set of columns:
CREATE OR REPLACE FUNCTION f_transpose (_tbl regclass, VARIADIC _cols text[])
RETURNS TABLE (id int, col text, val text) AS
$func$
BEGIN
RETURN QUERY EXECUTE format(
'SELECT t.id, v.* FROM %s t, unnest($1, ARRAY[%s]) v'
, _tbl, array_to_string(_cols, '::text,') || '::text'))
-- , _tbl, array_to_string(_cols, ','))) -- simple alternative for only text
USING _cols;
END
$func$ LANGUAGE plpgsql;
Call - with table name and any number of column names, any data types:
SELECT * FROM f_transpose('table_name', 'column1', 'column2', 'column3');
Weakness: the list of column names is not safe against SQL injection. You could gather column names from pg_attribute instead. Example:
How to perform the same aggregation on every column, without listing the columns?
SELECT id
,unnest(string_to_array('col1,col2,col3', ',')) col_name
,unnest(string_to_array(col1 || ',' || col2 || ',' || col3, ',')) val
FROM t
Try following method:
My sample table name is t,to get the n columns name you can use this query
select string_agg(column_name,',') cols from information_schema.columns where
table_name='t' and column_name<>'id'
this query will selects all columns in your table except id column.If you want to specify schema name then use table_schema='your_schema_name' in where clause
To create select query dynamically
SELECT 'select id,unnest(string_to_array(''' || cols || ''','','')) col_name,unnest(string_to_array(' || cols1 || ','','')) val from t'
FROM (
SELECT string_agg(column_name, ',') cols -- here we'll get all the columns in table t
,string_agg(column_name, '||'',''||') cols1
FROM information_schema.columns
WHERE table_name = 't'
AND column_name <> 'id'
) tb;
And using following plpgsql function dynamically creates SELECT id,unnest(string_to_array('....')) col_name,unnest(string_to_array(.., ',')) val FROM t and execute.
CREATE OR replace FUNCTION fn ()
RETURNS TABLE (
id INT
,columname TEXT
,columnvalues TEXT
) AS $$
DECLARE qry TEXT;
BEGIN
SELECT 'select id,unnest(string_to_array(''' || cols || ''','','')) col_name,unnest(string_to_array(' || cols1 || ','','')) val from t'
INTO qry
FROM (
SELECT string_agg(column_name, ',') cols
,string_agg(column_name, '||'',''||') cols1
FROM information_schema.columns
WHERE table_name = 't'
AND column_name <> 'id'
) tb;
RETURN QUERY
EXECUTE format(qry);
END;$$
LANGUAGE plpgsql
Call this function like select * from fn()

Customize SQL query

I have requirement where in I am creating a table dynamically(number of columns may change based on input parameter to my procedure through which I am creating this table) with data in the table like below.
PK col1 col2 col3
A null 1-2 3-4
B null null 4-5
C null 5-6 null
Now the requirement is I want to extract only the columns where at least there should be 1 record without null and spool the whole data into a file. My output should be like below (col1 exlluded from output as it has all nulls).
PK col2 col3
A 1-2 3-4
B null 4-5
C 5-6 null
Can anybody provide any hints to achieve this. Thanks in advance.
This won't be very efficient I suspect, but you can use COUNT() to determine if there are any only NULLS in a column because COUNT(column_here) will only add 1 for each non-null value. Hence if the count is zero that column is only NULLs.
This can then be combined into a query to generate a valid select statement and then that executed immediate (being careful of course to avoid sql injection).
Anyway, here's an example:
select
'select '
|| substr((
select
case when count(COL1) > 0 then ',col1' else '' end
|| case when count(COL2) > 0 then ',col2' else '' end
|| case when count(COL3) > 0 then ',col3' else '' end
from a_table
),2,8000)
|| ' from '
|| ' a_table'
as sql_string
from dual
;
see this sqlfiddle
result by the above is:
| SQL_STRING |
|--------------------------------|
| select col2,col3 from a_table |
Here is a try. First, create a function to generate your query, returning a REF CURSOR:
create or replace function select_non_nulls() return sys_refcursor as
myQuery varchar2(500);
myCur sys_refcursor;
begin
select 'select ' || listagg(col, ', ') within group (order by col) || ' from test'
into myQuery
from
(
select case when max(col1) is null then null else 'col1' end col from test
union all
select case when max(col2) is null then null else 'col2' end col from test
union all
select case when max(col3) is null then null else 'col3' end col from test
)
;
open myCur for myQuery;
return myCur;
end;
/
Then use it in SQL*Plus:
SQL> var rc refcursor
SQL> exec :rc := select_non_nulls;
SQL> print rc;
I used num_nulls from all_tab_cols and achieved result as per my requirement. Thank you.