Are these all valid methods of checking that a value exists and assigning null?
Insert ...
Select
ISNULL(ERROR_PROCEDURE(),''),
COALESCE(N' ', N' '),
'' STATUS,
This is for SQL Server 2008 R2.
Related
I am writing 1 PostgreSQL function for some operation.
Writing SQL migration for that function but facing formatting error as liquibase is not able to recognize some portion.
Function Liquibase Migration:
CREATE OR REPLACE FUNCTION schema.fncn(trId integer, sts integer, stIds character varying)
RETURNS double precision
LANGUAGE plpgsql
AS '
DECLARE
abc integer;
query CHAR(1500);
xyz integer;
BEGIN
query := ''select sum(t.a)
FROM schema.tbl t
where t.id in(1,2)
and t.status ='' || sts ||
'' and t.status <> 2
and t.tr_id ='' || trId ||
'' and t.sw in('''', ''N'')'';
IF stIds is not null then
query := query || '' AND t.st_id IN ('' || stIds || '')'';
ELSE
END IF;
EXECUTE query INTO abc;
SELECT abc INTO xyz;
RETURN xyz;
END;
'
;
Following error it throwing:
Caused by: org.postgresql.util.PSQLException: ERROR: syntax error at or near "N"
Reason: liquibase.exception.DatabaseException: ERROR: syntax error at or near "N"
Any suggestion what I am missing?
The immediate problem is the nesting of ' of single quotes. To make that easier, use dollar quoting for the function body. You can nest dollar quoted string by choosing different delimiters.
To avoid any problems with concatenation of parameters, use parameter place holders in the query and pass the values with the USING clause. That will however require two different execute calls.
I assume stIds is a comma separated string of values. To use that as a (single) placeholder, convert it to an array using string_to_array() - or even better: change the type of the input parameter to text[] and pass an array directly.
The query variable is better defined as text, don't use char. There is also no need to copy the result of the query into a different variable (which by the way would be more efficient using xyz := abc; rather than a select into)
CREATE OR REPLACE FUNCTION schema.fncn(trId integer, sts integer, stIds character varying)
RETURNS double precision
LANGUAGE plpgsql
AS
$body$
DECLARE
abc integer;
query text;
BEGIN
query := $q$ select sum(t.a)
FROM schema.tbl t
where t.id in (1,2)
and t.status = $1
and t.status <> 2
and t.tr_id = $2
and t.sw in ('''', 'N') $q$;
IF stIds is not null then
query := query || $sql$ AND t.st_id = ANY (string_to_array($4, ',') $sql$;
EXECUTE query INTO abc
using trid, sts, stids;
ELSE
EXECUTE query INTO abc
using trid, sts;
END IF;
RETURN abc;
END;
$body$
;
Note that in the Liquibase change, you must use splitStatements=false in order to run this without errors.
Can someone tell me why i get this error and how i can solve it?
I have a function like this:
CREATE OR REPLACE FUNCTION custom.dblink_function_v21()
RETURNS TABLE(name character varying, service character varying, scopes character varying)
LANGUAGE plpgsql
AS $function$
DECLARE
var_req TEXT;
rec_key record;
cur_key CURSOR FOR Select * from dblink(
'host=server1
user=user1
password=pw1
dbname=db1
port=5433',
'
select s.id,s.name,s.host from servers s where s.id IN (465,325) order by s.name
') as (srv character varying,name character varying, host character varying);
BEGIN
open cur_key;
loop
fetch cur_key into rec_key;
EXIT WHEN NOT FOUND;
var_req := 'Select * from dblink(
''host=' || rec_key.host || '
user=user2
password=pw2
dbname=db2'',
''
select
' || rec_key.name || ' as name ,
es.name as service,
es.scopes::varchar as scopes
from services es
'') as (srv varchar,service varchar,scopes varchar);
';
return query execute var_req;
end loop;
close cur_key;
END
$function$
;
when i call it with select * from custom.dblink_function_v21() i get this error :
SQL Error [42703]: ERROR: column "dba01" does not exist
Where: Error occurred on dblink connection named "unnamed": could not execute query.
PL/pgSQL function custom.dblink_function_v21() line 43 at RETURN QUERY
dba01 is not a column.. it's my first value in column name.
If i just change the ' || rec_key.name || ' too ' || rec_key.srv || ' i suddenly works. But i don't want the id of the server in return i want the name. They both have same datatype and everything so i don't whats the Problem.
Ive been trying different things like crazy but nothing works..
Working with postgres v.10
This is the select statement you create:
select XXX as name , es.name as service, es.scopes::varchar as scopes from services es
where XXX is the value of rec_key.name - and the value of rec_key.srv in the latter case.
In the former case, the statement evaluates to:
select dba01 as name , es.name as service, es.scopes::varchar as scopes from services es
but in the latter case:
select 100 as name , es.name as service, es.scopes::varchar as scopes from services es
In the former case, the error is issued as the column dba01 does not exist. In the latter case, the number 100 is selected as name. It is a perfectly valid statement as it is not interpreted as a column name.
If you want to select the text value "dba01" as column "name" you can change that part to:
''' || rec_key.name || '''::text as name ,
Best regards, Bjarni
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...
I have my function to run SELECT query with 3 condition of HAVING CLAUSE:
having sum() > 0
having sum() <= 0
dont have HAVING CLAUSE
Here is my function:
DROP function getf(arg int);
create or replace function getf(arg int)
returns table (
option_id bigint,
importQuantity bigint,
sold bigint,
remain bigint
)
as $$
begin
if arg = 1 then
return query select b.option_id, SUM(b.import_quantity)::bigint as importQuantity, SUM(b.sold_quantity)::bigint as sold, SUM(b.remaining_quantity)::bigint as remain from batch b where b.product_id = 220 and b.option_id in (select o.id from "option" o where o.barcode like '%%' or o.barcode is null) group by b.option_id having sum(b.remaining_quantity) > 0;
elsif arg = 2 then
return query select b.option_id, SUM(b.import_quantity)::bigint as importQuantity, SUM(b.sold_quantity)::bigint as sold, SUM(b.remaining_quantity)::bigint as remain from batch b where b.product_id = 220 and b.option_id in (select o.id from "option" o where o.barcode like '%%' or o.barcode is null) group by b.option_id having sum(b.remaining_quantity) <= 0;
elsif arg = 3 then
return query select b.option_id, SUM(b.import_quantity)::bigint as importQuantity, SUM(b.sold_quantity)::bigint as sold, SUM(b.remaining_quantity)::bigint as remain from batch b where b.product_id = 220 and b.option_id in (select o.id from "option" o where o.barcode like '%%' or o.barcode is null) group by b.option_id;
end if;
end; $$ language plpgsql;
And I call my function:
select getf(3);
Question
The function work fine. But SELECT query only different at HAVING CLAUSE
How can I use dynamic query to appending HAVING with if-else condition?
Since you need to change the structure of the query not just replace a parameter value within the query you need dynamic SQL. But first lets put on a diet. That is remove unnecessary parts.
b.option_id in (select o.id from "option" o where o.barcode like '%%' or o.barcode is null)
This is unnecessary the WHERE clause contains a tautology (a statement that in always true). Why is this. Well the predicate o.barcode like '%%' actually check if the barcode contains 0 or more characters. Only NULL makes this false, but in that case the other predicate barcode is NULL will evaluate true,so the overall condition is always true. As a result the sub-query always returns as ALL ids in options table and b.option_id is always in the list. (That of course assumes batch.option_id is properly defined as not null and a FK to options).
Now lets consider that HAVING clause. The first two are trivial replacements, just replace the rvalue with '< 0' or '>= 0'. The third option presents a problem, as you need to remove the entire clause rather change the rvalue. Its not all that difficult, however it also can be turned into a simple rvalue change. The HAVING predicate simply needs to evaluate true and we accomplish the same thing. That is achieved by replacing it by 'is not null'. Finally we can create an array of the rvalue replacements avoid any if then logic on the parameter (arg). Other that validating it contains a valid value. So: see demo
create or replace function getf(arg int)
returns table (option_id bigint
,importQuantity bigint
,sold bigint
,remain bigint
)
language plpgsql
as $$
declare
k_having_arg constant text[] = array['> 0','<= 0', 'is not null']; -- Replacement values for RVALUE below
k_base_query constant text =
'select b.option_id'
', SUM(b.import_quantity)::bigint '
', SUM(b.sold_quantity)::bigint '
', SUM(b.remaining_quantity)::bigint '
' from batch b'
' where b.product_id = 220'
' group by b.option_id '
' having sum(b.remaining_quantity) %s; '; -- expressions RVALUE to be replaces
l_exec_query text;
begin
if arg not between 1 and 3 then
raise exception 'Invalid Arg value (%) out of range, Must be 1, 2, or 3.', arg;
end if;
l_exec_query = format (k_base_query,k_having_arg[arg]);
raise notice E'Running query\n%',l_exec_query;
return query execute l_exec_query;
end;
$$;
try using with CTE clause. you can have your query (till group by) in with clause.
Then use can use your if conditions and select from with CTE and and append having clause.
For reference you can check
How to declare a variable in a PostgreSQL query
The function below expects following string
22.03.2016
28.02.2017 00:00:00
NULL
Sometimes the column contains garbage like '22.003.2016'.
How to manage this situation in T-SQL with SQL Server 2008 R2?
ALTER FUNCTION [dbo].[GetItemDocDetailDataInput]
(#ItemDocDetailID uniqueidentifier)
RETURNS datetime2(7)
AS
BEGIN
DECLARE #str nvarchar(20);
DECLARE #dataInput datetime2(7);
SELECT #str = ISNULL([DataInput], null)
FROM
(SELECT
ISNULL([Value], '') AS [Value],
CASE
WHEN [Value] IS NULL THEN NULL
ELSE SUBSTRING([Value], 0, 11)
END AS DataInput
FROM
ItemDocDetailParams
WHERE
ItemDocDetailID = #ItemDocDetailID
AND ItemParamTypeID = 12) D
SELECT #dataInput = CONVERT(datetime2(7), #str, 103)
RETURN #dataInput;
END
I assume the best approach is create another tsql function where I could check for dot positions in strings like
22.03.2016
28.02.2017 00:00:00
So I will use combination of following statements
SELECT CHARINDEX('.', '13.05.2019') AS FirstDot;
3 -- ОК
SELECT CHARINDEX('.', '13.05.2019', 4) AS SecondDot;
6 -- ОК
SELECT CHARINDEX('.', '13.005.2019', 4) AS MatchPosition;
7 --- NOT OK