Recursive SQL function, <= operator doesn't work - sql-server-2012-express

I just wonder why this works. But what I have comment doesn't work.
ALTER PROC spFactorial
#ValueIn int,
#ValueOut int OUTPUT
AS
DECLARE #InWorking int;
DECLARE #OutWorking int;
IF #ValueIn <> 1 -- this <= doesn't work??
BEGIN
SELECT #InWorking = #ValueIn - 1;
EXEC spFactorial #InWorking, #OutWorking OUTPUT;
SELECT #ValueOut = #ValueIn * #OutWorking;
END
ELSE
BEGIN
SELECT #ValueOut = 1;
END
RETURN;
GO
DECLARE #WorkingOut int;
DECLARE #WorkingIn int;
SELECT #WorkingIn = 3;
EXEC spFactorial #WorkingIn, #WorkingOut OUTPUT;
PRINT CAST(#WorkingIn AS varchar ) + ' factorial is '
+ CAST(#WorkingOut AS VARCHAR );
This IF #ValueIn <= 1 doesn't work - why?
In C# or java it works.
Thank you

<= is not an operator in SQL. In c# and Java it is.
For <= operator functionality in Sql, you can do this
IF(#value < 1 OR #value = 1)

Related

Postgresql Simple IF ELSE Statement

In MS SQL I can execute a statement like:
Declare #foo int = 1, #foo1 int = 42
IF #foo <> 0
BEGIN
SELECT #foo
END
ELSE
BEGIN
SELECT #foo, #foo1
END
Does anyone have any idea how to run this statement on postgresql?
EDIT: MS SQL Example like :
CREATE PROCEDURE dbo.spIFtest
#p1 int = 1,
#p2 int = 10,
#isFilter bit = 0
AS
BEGIN
IF #isFilter = 1
BEGIN
SELECT idx FROM rw.octest where idx between #p1 and #p2
END
ELSE
BEGIN
SELECT idx FROM rw.octest
END
END
GO
Using DO With caveats:
DO $$
DECLARE
foo integer := 1;
foo1 integer := 42;
BEGIN
IF foo <> 0 THEN
PERFORM foo;
ELSE
PERFORM foo, foo1;
END IF;
END;
$$
;
DO cannot return anything.
You can fake a return:
DO $$
DECLARE
foo integer := 0;
foo1 integer := 42;
BEGIN
IF foo <> 0 THEN
SELECT INTO foo 1;
RAISE NOTICE 'foo is %', foo;
ELSE
SELECT INTO foo, foo1 1, 42 ;
RAISE NOTICE 'foo is %, foo1 is %', foo, foo1;
END IF;
END;
$$
;
NOTICE: foo is 1, foo1 is 42
DO
In PostgreSQL DO Block can execute the queries but they can not return any value.
So the first part of your question is not possible directly in postgresql.
For second part of your question: In PostgreSQL you can use Function (which is very powerful and effective) like below:
create or replace function spiftest()
returns table(idx_ int)
as $$
declare
p1 int := 1;
p2 int := 10;
isfilter boolean := 0;
begin
if isfilter then
return query
SELECT idx FROM octest where idx between p1 and p2;
else
return query
SELECT idx FROM octest ;
end if;
end;
$$
language plpgsql
calling above function for result:
select * from spiftest()
You can write it with parameters also like below:
create or replace function spiftest(p1 int, p2 int, isfilter boolean)
returns table(idx_ int)
as $$
begin
if isfilter then
return query
SELECT idx FROM octest where idx between p1 and p2;
else
return query
SELECT idx FROM octest ;
end if;
end;
$$
language plpgsql
to call above function
select * from spiftest(1,10,'t')

INTERVAL add i day is not working in postgresql

I have a table like this:
CREATE TABLE DateInsert(
DateInsert timestamp without time zone,
DateInt integer NOT NULL
);
I want insert list day from 2018-01-01 to 2045-05-18 but it give me an erro
"invalid input syntax for type interval:"
CREATE OR REPLACE FUNCTION insertdate() RETURNS integer AS $$
DECLARE i integer := 0;
d timestamp without time zone := '2018-01-01';
di integer := 0;
BEGIN
while i <10000
LOOP
d := d + INTERVAL ''+ i::character varying + ' day';
di := to_char(d , 'yyyymmdd')::int;
insert into DateInsert(DateInsert,DateInt) values(d, di);
i := i+1;
END LOOP ;
return i;
END;
$$ LANGUAGE plpgsql;
How can I insert to db with timestamp increase 1 in n day loop?
Code In sql server has been working.
declare #i int=0
declare #d datetime
declare #di int = 0
while #i <10000
begin
set #d = DATEADD(DAY, #i, '2018-01-01')
set #di = cast(CONVERT(VARCHAR(10), #d, 112) as int)
insert into DateInsert(DateInsert,DateInt) values(#d, #di)
set #i = #i+1
end
The concatenation operator is || not +. And the prefixed form doesn't seem to like anything else than literals. But you can cast the concatenation expression.
So changing
...
d := d + INTERVAL ''+ i::character varying + ' day';
...
to
...
d := d + (i || ' day')::interval;
...
should work for you.

How to pass stored procedure parameter into EXECUTE statement

CREATE OR REPLACE FUNCTION "Test"(character varying[],character varying[])
RETURNS refcursor AS
$BODY$
DECLARE
curr refcursor;
filter text;
counter integer;
BEGIN
counter = 1;
filter = '';
IF array_length($1,1) > 0 THEN
filter = 'AND ';
WHILE ($1[counter] <> '') LOOP
filter = filter||'LOWER('||$1[counter]||'::character varying) LIKE ''%''||LOWER($2['||counter||'])||''%'' AND ';
counter = counter + 1;
END LOOP;
filter = substring(filter FROM 1 FOR (char_length(filter)-4));
OPEN curr FOR
EXECUTE 'SELECT "Reservation".* FROM "Reservation" WHERE "Reservation"."id" > 0 '||filter;
return curr;
END IF;
END
$BODY$
LANGUAGE plpgsql VOLATILE
COST 100;
SELECT "Test"(ARRAY['"Reservation"."status"'],'{"waiting"}');
FETCH ALL IN "<unnamed portal 1>";
I tried to print out the query:
"SELECT "Reservation".* FROM "Reservation" WHERE "Reservation"."id" > 0 AND LOWER("Reservation"."status"::character varying) LIKE '%'||LOWER($2[1])||'%' "
But when it's executed it said that there was no parameter $2. So I realize that it can't access that stored procedure's parameter.
I don't have to worry about the first parameter of sql injection since it's hard coded. But the second param has to be passed into the execution. How do I do that?
I've found out that I could pass the parameter into EXECUTE using the "USING" statement.
Here's the final working code:
CREATE OR REPLACE FUNCTION "Test"(character varying[],character varying[])
RETURNS refcursor AS
$BODY$
DECLARE
curr refcursor;
filter text;
counter integer;
BEGIN
counter = 1;
filter = '';
IF array_length($1,1) > 0 THEN
filter = 'AND ';
WHILE ($1[counter] <> '') LOOP
filter = filter||'LOWER('||$1[counter]||'::character varying) LIKE ''%''||LOWER($1['||counter||'])||''%'' AND ';
counter = counter + 1;
END LOOP;
filter = substring(filter FROM 1 FOR (char_length(filter)-4));
OPEN curr FOR
EXECUTE 'SELECT "Reservation".* FROM "Reservation" WHERE "Reservation"."id" > 0 '||filter USING $2;
return curr;
END IF;
END
$BODY$
LANGUAGE plpgsql VOLATILE
COST 100;
SELECT "Test"(ARRAY['"Reservation"."status"'],ARRAY['no-show']);
FETCH ALL IN "<unnamed portal 1>";
Note that I have $1 as the value in the EXECUTE statement, because it accepts $2 as its first parameter.

Add comma every nth character in value

my problem is pretty simple. I get a value from a sql select which looks like this:
ARAMAUBEBABRBGCNDKDEEEFOFIFRGEGRIEISITJPYUCAKZKG
and I need it like this:
AR,AM,AU,BE,BA,BR,BG,CN,DK,DE,EE,FO,FI,FR,GE,GR,IE,IS,IT,JP,YU,CA,KZ,KG
The length is different in each dataset.
I tried it with format(), stuff() and so on but nothing brought me the result I need.
Thanks in advance
With a little help of a numbers table and for xml path.
-- Sample table
declare #T table
(
Value nvarchar(100)
)
-- Sample data
insert into #T values
('ARAMAU'),
('ARAMAUBEBABRBGCNDKDEEEFOFIFRGEGRIEISITJPYUCAKZKG')
declare #Len int
set #Len = 2;
select stuff(T2.X.value('.', 'nvarchar(max)'), 1, 1, '')
from #T as T1
cross apply (select ','+substring(T1.Value, 1+Number*#Len, #Len)
from Numbers
where Number >= 0 and
Number < len(T1.Value) / #Len
order by Number
for xml path(''), type) as T2(X)
Try on SE-Data
Time to update your resume.
create function DontDoThis (
#string varchar(max),
#count int
)
returns varchar(max)
as
begin
declare #result varchar(max) = ''
declare #token varchar(max) = ''
while DATALENGTH(#string) > 0
begin
select #token = left(#string, #count)
select #string = REPLACE(#string, #token, '')
select #result += #token + case when DATALENGTH(#string) = 0 then '' else ',' end
end
return #result
end
Call:
declare #test varchar(max) = 'ARAMAUBEBABRBGCNDKDEEEFOFIFRGEGRIEISITJPYUCAKZKG'
select dbo.DontDoThis(#test, 2)
gbn's comment is exactly right, if not very diplomatic :) TSQL is a poor language for string manipulation, but if you write a CLR function to do this then you will have the best of both worlds: .NET string functions called from pure TSQL.
I believe this is what QQping is looking for.
-- select .dbo.DelineateEachNth('ARAMAUBEBABRBGCNDKDEEEFOFIFRGEGRIEISITJPYUCAKZKG',2,',')
create function DelineateEachNth
(
#str varchar(max), -- Incoming String to parse
#length int, -- Length of desired segment
#delimiter varchar(100) -- Segment delimiter (comma, tab, line-feed, etc)
)
returns varchar(max)
AS
begin
declare #resultString varchar(max) = ''
-- only set delimiter(s) when lenght of string is longer than desired segment
if LEN(#str) > #length
begin
-- continue as long as there is a remaining string to parse
while len(#str) > 0
begin
-- as long as know we still need to create a segment...
if LEN(#str) > #length
begin
-- build result string from leftmost segment length
set #resultString = #resultString + left(#str, #length) + #delimiter
-- continually shorten result string by current segment
set #str = right(#str, len(#str) - #length)
end
-- as soon as the remaining string is segment length or less,
-- just use the remainder and empty the string to close the loop
else
begin
set #resultString = #resultString + #str
set #str = ''
end
end
end
-- if string is less than segment length, just pass it through
else
begin
set #resultString = #str
end
return #resultString
end
With a little help from Regex
select Wow=
(select case when MatchIndex %2 = 0 and MatchIndex!=0 then ',' + match else match end
from dbo.RegExMatches('[^\n]','ARAMAUBEBABRBGCNDKDEEEFOFIFRGEGRIEISITJPYUCAKZKG',1)
for xml path(''))

Is there any hash function in PostgreSQL?

I am using Sphinx to index my database.
The problem is I have to filter the result by a character varying field.
So I have to find a way to convert character varying to sql_attr_uint.
I know that CRC32 in mysql can do the trick. Is there a CRC32 or any replacement in PostgreSQL?
This is the CRC32 function that defines thinking sphinx (gem):
CREATE OR REPLACE FUNCTION crc32(word text)
RETURNS bigint AS $$
DECLARE tmp bigint;
DECLARE i int;
DECLARE j int;
DECLARE byte_length int;
DECLARE word_array bytea;
BEGIN
IF COALESCE(word, '') = '' THEN
return 0;
END IF;
i = 0;
tmp = 4294967295;
byte_length = bit_length(word) / 8;
word_array = decode(replace(word, E'\\\\', E'\\\\\\\\'), 'escape');
LOOP
tmp = (tmp # get_byte(word_array, i))::bigint;
i = i + 1;
j = 0;
LOOP
tmp = ((tmp >> 1) # (3988292384 * (tmp & 1)))::bigint;
j = j + 1;
IF j >= 8 THEN
EXIT;
END IF;
END LOOP;
IF i >= byte_length THEN
EXIT;
END IF;
END LOOP;
return (tmp # 4294967295);
END
$$ IMMUTABLE LANGUAGE plpgsql;
Maybe you can use decode(substring(md5('foo') for 8), 'hex'). This would get you bytea of first 4 bytes of md5 hash of this string.
You can convert it to integer using something like:
create function bytea_to_integer(bytea)
returns integer strict
language sql as $$
select
(get_byte($1,0)*1::integer<<0*8)
+(get_byte($1,1)*1::integer<<1*8)
+(get_byte($1,2)*1::integer<<2*8)
+(get_byte($1,3)*1::integer<<3*8);
$$;