I want to do this:
select * from table_1 where table_1.column1 ilike '%value%' union
select * from table_1 where table_1.column2 ilike '%value%' union
select * from table_1 where table_1.column3 ilike '%value%';
but use a single variable for '%value%', something like this:
do $$
declare
my_var TEXT;
begin
my_var = '%value%';
select * from table_1 where table_1.column1 ilike my_var union
select * from table_1 where table_1.column2 ilike my_var union
select * from table_1 where table_1.column3 ilike my_var;
end $$;
but it doesn't seem to work (I'm kind of new to this) and I can't find the solution to do this. It doesn't have to be a do/end statement. I'm just trying to declare a variable to use more than once in the query, so that I don't have to copy and paste '%value%' 3 times every time I want to change it (keep in mind this %value% will always be the same; hence why I want it to be in my_var). Just want to change it once for all three statements and print out the same details.
https://www.postgresql.org/docs/current/sql-do.html
The code block is treated as though it were the body of a function
with no parameters, returning void. It is parsed and executed a single
time.
The name of the procedural language the code is written in. If
omitted, the default is plpgsql.
PL/pgsql reference: https://www.postgresql.org/docs/current/plpgsql.html
Since do command code block is returning void, we can use raise notice to extract/debug what we do. To print out something from console, generally we need some variable to hold what we want to compute/final result.
The following is an simple example to count the return query rows.
CREATE temp TABLE a1 (
column1 text,
column2 text,
column3 text
);
INSERT INTO a1
VALUES ('value', 'test', 'test1');
INSERT INTO a1
VALUES ('misc', 'value2', 'test2');
INSERT INTO a1
VALUES ('misc1', 'test3', 'value3');
DO $$
DECLARE
my_var text;
_count bigint;
BEGIN
my_var = '%value%';
SELECT
count(*) INTO _count
FROM (
SELECT * FROM a1 WHERE column1 ILIKE my_var
UNION
SELECT * FROM a1 WHERE column2 ILIKE my_var
UNION
SELECT * FROM a1 WHERE column3 ILIKE my_var
) cte;
RAISE NOTICE '_count = %', _count;
END
$$
LANGUAGE plpgsql;
I have a below SP which has sql query which need to refactor db2 query,in db2 i dont know how to concatenate the flag condition remaining query to main query.
CREATE PROCEDURE EMPLOYEE
(IN EMPID varchar(1000),
IN BFLAG char(3))
RESULT SETS 1
LANGUAGE SQL
P1: BEGIN
SET v_sql = 'select c.id,c.name from emp c'
IF BFLAG <> 'T' THEN
SET v_sql = v_sql ||
' left outer join dept U
where c.empid in (' || EMPID || ') ';
ELSE
SET v_sql = v_sql ||
' where c.empid in (' || EMPID || ') ';
END IF;
how to concatenate query in db2 based on flag value specified above condition.
DECLARE c_id CURSOR WITH RETURN FOR
select c.id,c.name from emp c;
Too many errors.
You should study how the compound-statement must be constructed.
Every variable must be declared.
The order of statements inside is significant: variable declarations first, then statements, cursors. SQL-procedure-statements are afterwards only.
You get erroneous dynamic statement if BFLAG <> 'T' is true: left join without conditions.
If the questions is how to use cursor with dynamic statements, then here is an example:
CREATE PROCEDURE TEST_DYNAMIC (IN P_REST varchar(1000))
DYNAMIC RESULT SETS 1
LANGUAGE SQL
P1: BEGIN
DECLARE v_sql VARCHAR (1000);
DECLARE v_s STATEMENT;
DECLARE v_c1 CURSOR WITH RETURN FOR v_s;
SET v_sql = 'select ' || P_REST;
PREPARE v_s FROM v_sql;
OPEN v_c1;
END
#
CALL TEST_DYNAMIC (' * FROM EMPLOYEE WHERE EMPNO IN (''000010'', ''000020'')')#
If not, then try to compile your routine without errors at least and show the runtime error / unexpected result if any...
Is it possible to iterate over a table's records and make a left join with them in a stored procedure?
Something like this:
FOR r IN SELECT tablename FROM tablewithtablenames ORDER BY tablename ASC
LOOP
INSERT INTO temp_Results
SELECT
temp_ids.Key as Key,
loggedvalue.pk_timestamp,
FROM
(temp_idS AS temp_ids
LEFT JOIN
quote_ident(r.tablename) AS loggedvalue
ON temp_ids.Key = loggedvalue.pk_fk_id);
END LOOP;
Unfortunately i get the following error message when i want to execute the stored procedure. (Function creation was successful.)
Error message:
ERROR: column loggedvalue.pk_fk_id does not exist LINE 29:
ON temp_ids.Key = "loggedvalue...
I have the feeling that i convert the record in a wrong way maybe because when i manually replaced the quote_ident(r.tablename) to the name of the table that i know the r contains it was fine, also i traced out the r.tablename in the loop and it was correct also.
As a_horse_with_no_name pointed out i should have use dynamic sql because in plpgsql you can not use a variable as a table name so i eliminated the loop and i used a union all:
CREATE OR REPLACE FUNCTION getaffectedtables(
OUT tableNames TEXT)
as $$
BEGIN
SELECT TRIM(TRAILING ' UNION ALL ' FROM string_agg('','SELECT * FROM "' || "tablename" || '" UNION ALL '))
INTO tableNames
FROM exampleTable;
END;$$
LANGUAGE plpgsql;
Then i used dynamic execute:
DECLARE
affectednames TEXT;
BEGIN
affectednames := getaffectedtables();
EXECUTE '
SELECT
temp_ids.Key as Key,
loggedvalue.pk_timestamp,
FROM
(temp_idS AS temp_ids
LEFT JOIN
('|| affectednames ||') AS loggedvalue
ON temp_ids.Key = loggedvalue.pk_fk_id);';
I'm a newbie in Posgresql
I have a table function:
CREATE OR REPLACE FUNCTION stage.get_primary_key_info(
schemaName text,
tableName text
) RETURNS TABLE(constraint_name text, column_name text) AS
$BODY$
SELECT c.constraint_name, c.column_name
FROM information_schema.key_column_usage AS c
LEFT JOIN information_schema.table_constraints AS t
ON t.constraint_name = c.constraint_name
WHERE t.table_schema = schemaName
AND t.table_name = tableName
AND t.constraint_type = 'PRIMARY KEY'
;
$BODY$ LANGUAGE sql;
And I'm trying to use this function like:
FOR c IN (SELECT * FROM stage.get_primary_key_info(target_schema, stmt.tablename))
LOOP
joinFields = joinFields || FORMAT('t.%s = s.%s AND', c.column_name);
END LOOP;
But I have this error:
The loop variable of the tuples must be a variable of the type record
or tuple or a list of scalar variables
Try declaring c as a record. You are also missing a second parameter in the FORMAT string.
Also, take a look at string_agg and you might be able to skip that loop entirely. Here is an example (substituting y for the second parameter):
SELECT string_agg(FORMAT('t.%s = s.%s', column_name, 'y'), ' AND ')
FROM get_primary_key_info(target_schema, stmt.tablename)
;
I am trying to create crosstab queries in PostgreSQL such that it automatically generates the crosstab columns instead of hardcoding it. I have written a function that dynamically generates the column list that I need for my crosstab query. The idea is to substitute the result of this function in the crosstab query using dynamic sql.
I know how to do this easily in SQL Server, but my limited knowledge of PostgreSQL is hindering my progress here. I was thinking of storing the result of function that generates the dynamic list of columns into a variable and use that to dynamically build the sql query. It would be great if someone could guide me regarding the same.
-- Table which has be pivoted
CREATE TABLE test_db
(
kernel_id int,
key int,
value int
);
INSERT INTO test_db VALUES
(1,1,99),
(1,2,78),
(2,1,66),
(3,1,44),
(3,2,55),
(3,3,89);
-- This function dynamically returns the list of columns for crosstab
CREATE FUNCTION test() RETURNS TEXT AS '
DECLARE
key_id int;
text_op TEXT = '' kernel_id int, '';
BEGIN
FOR key_id IN SELECT DISTINCT key FROM test_db ORDER BY key LOOP
text_op := text_op || key_id || '' int , '' ;
END LOOP;
text_op := text_op || '' DUMMY text'';
RETURN text_op;
END;
' LANGUAGE 'plpgsql';
-- This query works. I just need to convert the static list
-- of crosstab columns to be generated dynamically.
SELECT * FROM
crosstab
(
'SELECT kernel_id, key, value FROM test_db ORDER BY 1,2',
'SELECT DISTINCT key FROM test_db ORDER BY 1'
)
AS x (kernel_id int, key1 int, key2 int, key3 int); -- How can I replace ..
-- .. this static list with a dynamically generated list of columns ?
You can use the provided C function crosstab_hash for this.
The manual is not very clear in this respect. It's mentioned at the end of the chapter on crosstab() with two parameters:
You can create predefined functions to avoid having to write out the
result column names and types in each query. See the examples in the
previous section. The underlying C function for this form of crosstab
is named crosstab_hash.
For your example:
CREATE OR REPLACE FUNCTION f_cross_test_db(text, text)
RETURNS TABLE (kernel_id int, key1 int, key2 int, key3 int)
AS '$libdir/tablefunc','crosstab_hash' LANGUAGE C STABLE STRICT;
Call:
SELECT * FROM f_cross_test_db(
'SELECT kernel_id, key, value FROM test_db ORDER BY 1,2'
,'SELECT DISTINCT key FROM test_db ORDER BY 1');
Note that you need to create a distinct crosstab_hash function for every crosstab function with a different return type.
Related:
PostgreSQL row to columns
Your function to generate the column list is rather convoluted, the result is incorrect (int missing after kernel_id), it can be replaced with this SQL query:
SELECT 'kernel_id int, '
|| string_agg(DISTINCT key::text, ' int, ' ORDER BY key::text)
|| ' int, DUMMY text'
FROM test_db;
And it cannot be used dynamically anyway.
#erwin-brandstetter: The return type of the function isn't an issue if you're always returning a JSON type with the converted results.
Here is the function I came up with:
CREATE OR REPLACE FUNCTION report.test(
i_start_date TIMESTAMPTZ,
i_end_date TIMESTAMPTZ,
i_interval INT
) RETURNS TABLE (
tab JSON
) AS $ab$
DECLARE
_key_id TEXT;
_text_op TEXT = '';
_ret JSON;
BEGIN
-- SELECT DISTINCT for query results
FOR _key_id IN
SELECT DISTINCT at_name
FROM report.company_data_date cd
JOIN report.company_data_amount cda ON cd.id = cda.company_data_date_id
JOIN report.amount_types at ON cda.amount_type_id = at.id
WHERE date_start BETWEEN i_start_date AND i_end_date
AND interval_type_id = i_interval
LOOP
-- build function_call with datatype of column
IF char_length(_text_op) > 1 THEN
_text_op := _text_op || ', ' || _key_id || ' NUMERIC(20,2)';
ELSE
_text_op := _text_op || _key_id || ' NUMERIC(20,2)';
END IF;
END LOOP;
-- build query with parameter filters
RETURN QUERY
EXECUTE '
SELECT array_to_json(array_agg(row_to_json(t)))
FROM (
SELECT * FROM crosstab(''SELECT date_start, at.at_name, cda.amount ct
FROM report.company_data_date cd
JOIN report.company_data_amount cda ON cd.id = cda.company_data_date_id
JOIN report.amount_types at ON cda.amount_type_id = at.id
WHERE date_start between $$' || i_start_date::TEXT || '$$ AND $$' || i_end_date::TEXT || '$$
AND interval_type_id = ' || i_interval::TEXT || ' ORDER BY date_start'')
AS ct (date_start timestamptz, ' || _text_op || ')
) t;';
END;
$ab$ LANGUAGE 'plpgsql';
So, when you run it, you get the dynamic results in JSON, and you don't need to know how many values were pivoted:
select * from report.test(now()- '1 week'::interval, now(), 1);
tab
----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
[{"date_start":"2015-07-27T08:40:01.277556-04:00","burn_rate":0.00,"monthly_revenue":5800.00,"cash_balance":0.00},{"date_start":"2015-07-27T08:50:02.458868-04:00","burn_rate":34000.00,"monthly_revenue":15800.00,"cash_balance":24000.00}]
(1 row)
Edit: If you have mixed datatypes in your crosstab, you can add logic to look it up for each column with something like this:
SELECT a.attname as column_name, format_type(a.atttypid, a.atttypmod) AS data_type
FROM pg_attribute a
JOIN pg_class b ON (a.attrelid = b.relfilenode)
JOIN pg_catalog.pg_namespace n ON n.oid = b.relnamespace
WHERE n.nspname = $$schema_name$$ AND b.relname = $$table_name$$ and a.attstattarget = -1;"
I realise this is an older post but struggled for a little while on the same issue.
My Problem Statement:
I had a table with muliple values in a field and wanted to create a crosstab query with 40+ column headings per row.
My Solution was to create a function which looped through the table column to grab values that I wanted to use as column headings within the crosstab query.
Within this function I could then Create the crosstab query. In my use case I added this crosstab result into a separate table.
E.g.
CREATE OR REPLACE FUNCTION field_values_ct ()
RETURNS VOID AS $$
DECLARE rec RECORD;
DECLARE str text;
BEGIN
str := '"Issue ID" text,';
-- looping to get column heading string
FOR rec IN SELECT DISTINCT field_name
FROM issue_fields
ORDER BY field_name
LOOP
str := str || '"' || rec.field_name || '" text' ||',';
END LOOP;
str:= substring(str, 0, length(str));
EXECUTE 'CREATE EXTENSION IF NOT EXISTS tablefunc;
DROP TABLE IF EXISTS temp_issue_fields;
CREATE TABLE temp_issue_fields AS
SELECT *
FROM crosstab(''select issue_id, field_name, field_value from issue_fields order by 1'',
''SELECT DISTINCT field_name FROM issue_fields ORDER BY 1'')
AS final_result ('|| str ||')';
END;
$$ LANGUAGE plpgsql;
The approach described here worked well for me.
Instead of retrieving the pivot table directly. The easier approach is to let the function generate a SQL query string. Dynamically execute the resulting SQL query string on demand.