How to declare local variables in postgresql? - sql-server

There is an almost identical, but not really answered question here.
I am migrating an application from MS SQL Server to PostgreSQL. In many places in code I use local variables so I would like to go for the change that requires less work, so could you please tell me which is the best way to translate the following code?
-- MS SQL Syntax: declare 2 variables, assign value and return the sum of the two
declare #One integer = 1
declare #Two integer = 2
select #One + #Two as SUM
this returns:
SUM
-----------
3
(1 row(s) affected)
I will use Postgresql 8.4 or even 9.0 if it contains significant fetaures that will simplify the translation.

Postgresql historically doesn't support procedural code at the command level - only within functions. However, in Postgresql 9, support has been added to execute an inline code block that effectively supports something like this, although the syntax is perhaps a bit odd, and there are many restrictions compared to what you can do with SQL Server. Notably, the inline code block can't return a result set, so can't be used for what you outline above.
In general, if you want to write some procedural code and have it return a result, you need to put it inside a function. For example:
CREATE OR REPLACE FUNCTION somefuncname() RETURNS int LANGUAGE plpgsql AS $$
DECLARE
one int;
two int;
BEGIN
one := 1;
two := 2;
RETURN one + two;
END
$$;
SELECT somefuncname();
The PostgreSQL wire protocol doesn't, as far as I know, allow for things like a command returning multiple result sets. So you can't simply map T-SQL batches or stored procedures to PostgreSQL functions.

Related

Accessing to OUT parameter (t_cursor type) from stored procedure using go and InstantClient

I'm dealing with an Oracle DB, connecting from go via InstantClient (version 11) (https://github.com/mattn/go-oci8). I need to be able to load this object and browse results... t_cursor output parameter.
I have tried many strategies, I know how to map function parameters to go structures but I don't know how to work with t_cursor type since it seems not being implemented in InstantClient
Example of stored procedure
create or replace procedure EXAMPLE(a IN NUMBER, b IN NUMBER, c OUT T_CURSOR) AS BEGIN
[Edit] We have also tried to execute SQL blocks from code to try to handle this third parameter.
i.e.
If you add something like
declare
c t_cursor;
begin
EXAMPLE(:1, :2, c)
end
then I don't know how you can get the block to return a result set that contains the cursor.
declare
c t_cursor;
begin
EXAMPLE(:1, :2, c)
select 1, c from dual
end
The whole block returning the result of that select would be ideal but oracle blocks do not return result sets afaik.
Anyone who can bear a hand on this?
Thank you very much
It can be done with the driver https://github.com/rana/ora instead.
*Rset may be passed to Stmt.Exe when prepared with a stored procedure accepting an OUT SYS_REFCURSOR
The README.me even has that exact example.
Caveats:
It's unclear whether the database/sql interface may be used or you are limited to the lib specific API.
Instant Client gets restricted to versions from 12.1.0.1.0 on.

What is regexp_replace equivalent in SQL Server

I have this piece of code in Oracle which I need to convert into SQL Server to get the same behavior. I have used the REPLACE function. It seems to be working but I just wanted to make sure.
REGEXP_REPLACE(
phonenumber,
'([[:digit:]]{3})([[:digit:]]{3})([[:digit:]]{4})',
'(\1)\2-\3'
) phonenumber
As Martin said in his answer, SQL Server does not have built-in RegEx functionality (and while it has not been suggested here, just to be clear: no, the [...] wildcard of LIKE and PATINDEX is not RegEx). If your data has little to no variation then yes, you can use some combination of T-SQL functions: REPLACE, SUBSTRING, LEFT, RIGHT, CHARINDEX, PATINDEX, FORMATMESSAGE, CONCAT, and maybe one or two others.
However, if the data / input has even a moderate level of complexity, then the built-in T-SQL functions will be at best be cumbersome, and at worst useless. In such cases it's possible to do actual RegEx via SQLCLR (as long as you aren't using Azure SQL Database Single DB or SQL Server 2017+ via AWS RDS), which is (restricted) .NET code running within SQL Server. You can either code your own / find examples here on S.O. or elsewhere, or try a pre-done library such as the one I created, SQL# (SQLsharp), the Free version of which contains several RegEx functions. Please note that SQLCLR, being .NET, is not a POSIX-based RegEx, and hence does not use POSIX character classes (meaning: you will need to use \d for "digits" instead of [:digit:]).
The level of complexity needed in this particular situation is unclear as the example code in the question implies that the data is simple and uniform (i.e. 1112223333) but the example data shown in a comment on the question appears to indicate that there might be dashes and/or spaces in the data (i.e. xxx- xxx xxxx).
If the data truly is uniform, then stick with the pure T-SQL solution provided by #MartinSmith. But, if the data is of sufficient complexity, then please consider the RegEx example below, using a SQLCLR function found in the Free version of my SQL# library (as mentioned earlier), that easily handles the 3 variations of input data and more:
SELECT SQL#.RegEx_Replace4k(tmp.phone,
N'\(?(\d{3})\)?[ .-]*(\d{3})[ .-]*(\d{4})', N'($1)$2-$3',
-1, -- count (-1 == unlimited)
1, -- start at
N'') -- RegEx options
FROM (VALUES (N'8885551212'),
(N'123- 456 7890'),
(N'(777) 555- 4653')
) tmp([phone]);
returns:
(888)555-1212
(123)456-7890
(777)555-4653
The RegEx pattern allows for:
0 or 1 (
3 decimal digits
0 or 1 )
0 or more of , ., or -
3 decimal digits
0 or more of , ., or -
4 decimal digits
NOTE
It was mentioned that the newer Language Extensions might be a better choice than SQLCLR. Language Extensions allow calling R / Python / Java code, hosted outside of SQL Server, via the sp_execute_external_script stored procedure. As the Tutorial: Search for a string using regular expressions (regex) in Java page shows, external scripts are actually not a good choice for many / most uses of RegEx in SQL Server. The main problems are:
Unlike with SQLCLR, the only interface for external scripts is a stored procedure. This means that you can't use any of that functionality inline in a query (SELECT, WHERE, etc).
With external scripts, you pass in the query, work on the results in the external language, and pass back a static result set. This means that compiled code now has to be more specialized (i.e. tightly-coupled) to the particular usage. Changing how the query uses RegEx and/or what columns are returned now requires editing, compiling, testing, and deploying the R / Python / Java code in addition to (and coordinated with!) the T-SQL changes.
I'm sure external scripts are absolutely wonderful, and a better choice than SQLCLR, in certain scenarios. But they certainly do not lend themselves well to the highly varied, and often ad hoc, nature of how RegEx is used (like many / most other functions).
SQL Server does not have native regex support. You would need to use CLR (or as #Lukasz Szozda points out in the comments one of the newer Language Extensions) .
If I have understood the regex correctly though it matches strings of 10 digits and assigns the first 3 to group 1, second 3 to group 2, and last 4 to group 3 and then uses the back references in the expression (\1)\2-\3
You can use built in string functions to do this as below
SELECT CASE
WHEN phonenumber LIKE REPLICATE('[0-9]', 10)
THEN FORMATMESSAGE('(%s)%s-%s',
LEFT(phonenumber, 3),
SUBSTRING(phonenumber, 4, 3),
RIGHT(phonenumber, 4))
ELSE phonenumber
END
You can write SQL function using CLR, that will wrap standard dotnet regex. I have wrote this and you can use it there. It will look this:
DECLARE #SourceText NVARCHAR(MAX) = N'My first line <br /> My second line';
DECLARE #RegexPattern NVARCHAR(MAX) = N'([<]br\s*/[>])';
DECLARE #Replacement NVARCHAR(MAX) = N''
DECLARE #IsCaseSensitive BIT = 0;
SELECT regex.Replace(#SourceText, #RegexPattern, #Replacement, #IsCaseSensitive);

Dynamic cursor PERVASIVE

I'm trying to create SP with dynamic cursor for obtain the result of any Select statement
CREATE PROCEDURE CursorTest (:query IN VARCHAR(5000)) ;
BEGIN
DECLARE :out VARCHAR;
DECLARE :dynamicCursor CURSOR FOR EXEC (:query);
OPEN dynamicCursor;
/* cursor loop */
Cursorloop:
LOOP
FETCH NEXT FROM `enter code here`Cursorloop INTO :out;
End LOOP;
CLOSE dynamicCursor;
END;
I have 2 problems on that, Declare the cursor with the dynamic query and output the result as a row.
Thanks in advance
Since this question is tagged pervasive I'm assuming you want to achieve this in PervasiveSQL.
I don't think what you are trying to do is possible there. The main reason for this is that - to my knowledge - P-SQL has no aggregate functions to combine arbitrary columns or rows into a string (like e.g. PostgreSQL's string_agg).
Secondly, P-SQL does not support querying by column number. The :query argument can be any statement (even an invalid one!), so you don't know how many columns it'll produce.
On a more essential note: what is it exactly that you want to achieve? This stored procedure looks to me like an overly complicated way of just executing :query, and having no means of handling the result. If logging or analysis is your goal, wouldn't you be better off by using an external, more flexible (scripting) language to deal with the result set? Admittedly SQL is a programming language, but it has its limitations.

Safely converting a function into a procedure in SQL Server

I've been converting an oracle schema to an sql server one and got the following error
Invalid use of a side-effecting operator 'SET COMMAND' within a function.
In my case modifying the database involved this
set #originalDateFirst = ##DateFirst;
set datefirst 1;
set #DayOfWeek = datepart(weekday,#DATE); -- 1 to 5 = Weekday
set datefirst originalDateFirst;
Ideally this wouldn't have modified the database but the datepart function uses static state.
I'm not really from a database background so was slightly baffled by this but reading other answers it looked like all I needed to do was swap the word function for procedure and I'd be away. However I then got the following error
Incorrect syntax near 'RETURNS'.
Reading around a bit about stored procedures aren't allowed to return anything they like - only integers. However the integers normally have the same semantics as a console application's return code - 0 is success and anything else is an error.
Luckily the type I wanted to return was an integer so fixing the next error:
Incorrect syntax near 'RETURNS'.
Involved just removing
RETURNS INTEGER
from the function/procedure. However I'm unsure if there are any weird side effects caused by this error code interpretation that will be outside of my control. The function actually just returns either 0 or 1 basically as a true or false flag (where 1 is true and 0 is false as you might expect). Therefore one of my return values would count as an 'error'.
What if any are the consequences of piggybacking on the return code of a procedure rather than using an out parameter? Is it just a bad practice? If it's safe to do this I'd certainly prefer to so I don't need to change any calling code.
This isn't an answer to your question as posed, but may be a better solution to the overall problem.
Rather than having to rely on a particular DATEFIRST setting, or changing the DATEFIRST setting, why not use an expression that always returns reliable results no matter what the DATEFIRST setting is.
For example, this expression:
select (DATEPART(weekday,GETDATE()) + 7 - DATEPART(weekday,'20140406')) % 7
always returns 1 on Mondays, 2 on Tuesdays, ..., 5 on Fridays. No matter what settings are in effect.
So, your entire original block of 4 lines of code could just be:
set #DayOfWeek = (DATEPART(weekday,#Date) + 7 -
DATEPART(weekday,'20140406')) % 7; -- 1 to 5 = Weekday
And now you should be able to continue writing it as a function rather than a stored procedure.
If it's safe to do this I'd certainly prefer to so I don't need to change any calling code.
Which you would have to do if you did change your function into a stored procedure. There's no syntax where you can look at the call and ever be in doubt of whether a stored procedure or a function is being invoked - they always use different syntaxes. A procedure is executed by being the first piece of text in a batch or by being preceded by the EXEC keyword and no parentheses.
A function, on the other hand, always has to have parentheses applied when calling it, and must appear as an expression within a larger statement (such as SELECT). You cannot EXEC a function, nor call one by it being the first piece of text in a batch.
An out param could be of (almost) any valid datatype, RETURN is always an int, not necessarily 0 or 1.
Because you can't use a procedure as a query source (it's not a table), to consume a return value from a procedure, declare a variable and exec the procedure like this:
create procedure p as
-- some code
return 13
go
declare #r int
exec #r = p
select #r
I wouldn't call it piggybacking, it's a regular way to return a success/error code for example. But how you interprete the return value is entirely up to calling code.
Functions, otoh, can be used as a query source, if table-valued, or as a scalar value in select list or where clause etc. But you can't modify data inside functions, and there are other restrictions with them (as you've learned already). Furthermore, functions can have nasty impact on performance (except the inline table-valued functions, they're pretty much safe to use).

tSQLt - Handling multiple SQL Server result sets

I'm creating a SQL Server unit test using tSQLt.
The proc that I'm testing returns 3 result sets. My webAPI handles the multiple result sets and sends it to the UI fine.
Question: In my SQL Server unit test, how do I handle the 3 result sets? If the proc returns one result set, it is easy to handle. I use the following:
Insert Into #ReturnData
(
ID,
Data1,
Data2
)
Exec #Ret = StoreProcName
Then I can run a bunch of checks against the #ReturnData temp table. But I don't understand how to handle/test a proc if it returns multiple result sets. Is this even possible?
Thanks.
The method I'd suggest you use is tSQLt.ResultSetFilter(). This takes a parameter for number of the result set to return and calls your code under test (StoreProcName in your example), returning that result set, which you can then use Insert..Exec to capture.
The down side of this procedure is that it only captures that one result set per run - so you need to call it multiple times to return all of the result sets. I usually only look at one result set per test, allowing me to concentrate on answering one question in that test, but if your result sets interrelate and you need both to return for your test to be evaluated, then you will need to call tSQLt.ResultSetFilter and hence the code under test more than once in your test (the manual has more info on this situation)
As an aside, I have previously blogged about some unexpected behaviour I encountered when using insert..exec with SPs that return multiple identical result sets which may be of interest.
DaveGreen has the answer. But for completeness, I wanted to share this which expands on the basics: http://tsqlt.org/201/using-tsqlt-resultsetfilter/
If you call a stored procedure and need to pass in parameters, do the following:
Create a #Variable that holds the ‘exec …’ string with the parameter values embedded. Then you can do something like this:
Declare #Variable Varchar(max)
Set #Variable = ‘exec STOREDPROCNAME ‘’param1’’, ‘’param2’’’;
EXEC tSQLt.ResultSetFilter 2, #Variable
The number 2 specifies the second result set that is returned.
Nice and snappy ... ;-)

Resources