Run create function code without SQLCMD - sql

I have DDL code to create a function that runs flawlessly from sqlcmd and other specific SQL Server clients:
/*
* Émulo de la función LPAD() de Oracle
*/
CREATE FUNCTION LPAD
(
#cadena VARCHAR(MAX), -- Texto de partida
#tamano INT, -- Tamaño de la cadena resultante
#relleno CHAR -- Carácter de relleno
)
RETURNS VARCHAR(MAX)
AS
BEGIN
return REPLICATE(#relleno, #tamano - LEN(#cadena)) + #cadena;
END
GO
However, it won't typically run from cross-DBMS clients (SQL Fiddle, DBeaver, HeidiSQL...):
[102] Incorrect syntax near '#cadena'.
I suspect it's got to do with the GO batch separator not being implemented. If that's the case, is there an alternative syntax?

To sum up my comments:
The standard delimiter in SQL is the ;.
However, when running a code block that contains embedded semicolons (e.g. when creating a stored procedure) the standard delimiter cannot be applied as in that case the block wouldn't be sent as a single statement.
For those cases the SQL client usually has some way of defining an alternate delimiter that marks the end of a block and usually has a higher precedence than the ;
Microsoft calls this "alternate delimiter" the "batch separated" and decided to use GO for this and all Microsoft tools stick to that. In SSMS this batch separator can actually be configured to something else.
Oracle's SQL*Plus (and SQL Developer) use / for this.
Other SQL clients allow a dynamic definition inside a SQL script typically using delimiter (or something similar) to change the delimiter for subsequent statements.
As I have never used DBeaver or HeidiSQL I cannot tell how this is done in those SQL clients.

Related

DB2 Stored Procedure IF/ELSE -- What am I missing?

Trying to modify a stored procedure at work and looking to add some conditional logic into the procedure. I created a small test procedure just to try and understand IF/ELSE logic and I keep getting barked at by DBeaver (my SQL UI). I took the example pretty much directly off an IBM documentation web site. Can someone tell me what I'm missing here:
CREATE PROCEDURE IFTEST(IN rating INTEGER, OUT res VARCHAR(1))
LANGUAGE SQL
BEGIN
IF rating = 1 THEN
SET res = '2';
-- ELSE IF rating = 2 THEN
-- SET res = 3;
ELSE
SET res = '4';
END IF;
END
It keeps giving me this:
SQL Error [42601]: An unexpected token "END-OF-STATEMENT" was found following "s = '4'". Expected tokens may include: "
END IF".. SQLCODE=-104, SQLSTATE=42601, DRIVER=4.26.14
This is a frequently asked question.
First ensure your code is syntactically valid: in your case verify that each conditional statement has its own END IF, or alternatively use ELSEIF (and not ELSE IF) when you want to have a single conditional statement.
Next, verify your compound statement delimiters.
When you have a 'block' (multiple SQL statements) that executes as a single-statement (also known as compound SQL compiled), the Db2 client tool needs to recognize the end of the block.
The SQL standard does not specify how to do this, so different RDBMS tools use different techniques.
Many people who use Db2 will use the # character as the compound SQL block delimiter instead of the default character which is semi-colon. That semi-colon is still required to delimit the statements inside of the block, but the block also needs to have some delimiter, so if you use # then the final character in your stored-procedure should be that # character. You then select the block including this delimiter and ask DBeaver to execute it as a single compound statement.
DBeaver lets you do this configuration via its settings GUI.
Example navigation in DBeaver (depending on your version of DBeaver):
Properties > Editors > SQL Editor > SQL Processing
Statements delimiter: #
Ignore native delimiter (tick)
Blank line is statement delimiter (only use this if you code accordingly)
Remove trailing query delimiter (tick)
When you make these changes, you make need to disconnect the data source, and reconnect before they will be effective.
You can also make these changes in 'Global Settings' if you only use Db2 data sources and want these settings for all databases.
But again, you may need to disconnect and reconnect before the settings become effective.

Change delimiter in a DB2 script

I have setup DB2 v11.5.5.1 in a Docker container and accessed it via DBeaver.
I want to run a script with a CREATE PROCEDURE statement on DB2 database.
But it returns SQL Error [42601]: An unexpected token "END-OF-STATEMENT" was found following "E IF EXISTS TEMP_IDS".
The sample script I tried is here.
--#SET DELIMITER $$
CREATE PROCEDURE proc_name ()
BEGIN
-- some statements;
END$$
How can I solve this delimiter issue in DB2 script? I think the issue with changing the delimiter.
Your mistake is that you are trying to use a Db2 CLP (command line processor) feature inside DBEaver, i.e you are trying to use --#SET TERMINATOR....
This feature --#SET TERMINATOR is available if you execute your script with the Db2 clp (i.e. on MS-Windows that is db2.exe, and on linux/unix the db2 command in the shell).
For Dbeaver, a jdbc application, there is a different method to configure the alternative statement terminator / delimiter. Use the properties settings to find this option and choose a valid character in that GUI. Then retry.
For example, in Dbeaver Community Edition version 2021.04, use:
File > Properties > Editors > SQL Exitor > SQL Processing: "Statements Delimiter"

Can I prepare a statement in plain Oracle SQL?

3GLs provide mechanisms to prepare statements before executing them. E.g.
SELECT name
FROM people
WHERE age=:AGE
The same query can then be executed for different ages. But can such a statement also be prepared in a "plain" Oracle SQL client? Can the same be done in e.g. SQL Plus or dbForge Studio for Oracle as in Java or C# or any other programming language that supports prepared statements?
In dbForge Studio for Oracle, named parameters can be used, preceded by a colon :
SELECT *
FROM people
WHERE name=:name
The parameters can then be filled in with the "Edit parameters dialog box", available from the SQL toolbar.
I know you didn't ask about PostgreSQL but about Oracle. However, of note, PostgreSQL has this feature right in its SQL language.
The SQL standard includes a PREPARE statement, but it is only for use in embedded SQL. The PostgreSQL version of the PREPARE statement works like this:
PREPARE nameByAge(number) AS
SELECT name
FROM People
WHERE age=$1;
and you use it like this:
EXECUTE nameByAge(18);
EXECUTE nameByAge(50);
So unfortunately for Oracle SQLPlus the answer seems to be no, not bind variables. But SQLPlus has substitution variables, similar to shell scripts. You use them as &1, &2, &3, ... and they get their parameters from the way you call the SQLPlus script.
sqlplus user/password #script.sql 18
sqlplus user/password #script.sql 50
with the script.sql being
SELECT name
FROM People
WHERE age=&1;
this would work, even though it is not bind. But then, do you really care about the slight savings in repeat parse time? In fact Oracle hashes SQL statements and already replaces constants with bind variables to be able to better reuse query plans. So the savings you would get with PREPARE and BIND are really minuscule.

REPLACE function in T-SQL 2008 is different from T-SQL 2005

I am on project of migrating databases from SQL Server 2005 to 2008.
During test I found one inconsistency. In accordance to BOL http://msdn.microsoft.com/en-us/library/ms186862(v=SQL.100).aspx (2008) and http://msdn.microsoft.com/en-us/library/ms186862(v=SQL.90).aspx (2005) returns varchar. So far both are the same. However if we pass to REPLACE function column type char then difference comes out. Look at this code
declare #test table
(
testcharstring char(25)
)
insert into #test
select 'Hello'
union
select 'World'
union
select 'Hello world '
select
'"'+testcharstring+'"' as original
,'"'+replace(testcharstring,'a','A')+'"' as afterreplace
--,'"'+replace(rtrim(testcharstring),'a','A')+'"'
from #test
Result from SQL Server 2005
original afterreplace
--------------------------- ---------------------------
"Hello " "Hello"
"Hello world " "Hello world"
"World " "World"
Result from SQL Server 2008
original afterreplace
--------------------------- ---------------------------
"Hello " "Hello "
"Hello world " "Hello world "
"World " "World "
T-SQL in SQL Server 2005 removes even legitimate trailing space, not to say that it threats char(25) as varchar(25). T-SQL in SQL Server 2008 approaches type more carefully and returns results in accordance of type which it receives for transformation
I have number places in different T-SQL objects, mostly in triggers. Main idea just to make minimal changes to keep same behaviour in SQL Server 2008
Possible ways to do it
Override built-in REPLACE function Quick search suggests that it impossible however my teammate wants to research that option
Use Rtrim() functions together with REPLACE. This will require replacement in exact places in code in multiple routines (where char columns are used)
Creating own version Replace in CLR to see that CLR allows me to keep SQL Server 2005 behaviour and then again search and replace function in exact location
I would like to ask everybody if somebody came across of this issue, how did you worked out?
Also any suggestion is also welcome, may be I just do not know what settings on server instance or database level can change behaviour.
Thank you in advance!
You have different SET ANSI_PADDING options, which can also be controlled by SET ANSI_DEFAULTS
As it stands, REPLACE behaves the same in both editions. Both (2005, 2008) say:
Returns nvarchar if one of the input arguments is of the nvarchar data type; otherwise, REPLACE returns varchar.
Edit: there are 2 Connect bugs/features
My answer above is probably wrong
http://connect.microsoft.com/SQLServer/feedback/details/259840/trailing-spaces-are-lost-when-a-char-value-is-fed-to-replace
Check DB compatible level:
http://connect.microsoft.com/SQLServer/feedback/details/126092/t-sql-replace-function-seems-to-be-broken-for-char-x-variables
And as a fix, sorry, I'd use rtrim, however is it a fix? You can't override replace, and if you plan on a clr urgent, why not wrap the replace/rtrim in a SQL udf
according to MS this is a correct behavior and SQL2005 had it wrong.
in you code you are using Replace() not only as corection function (Find a pattern and replace with another pattern) but also as Trim() function (if nothing found at least trim the incoming value)
but this is wrong when you are working with Char(). the only reason to use Char() as data type is to preserve the values data length at all cost.(IMHO) as in you need to ensure that returning Value length is ALWAYS the same regardless of actual stored character count.
this is important when you need to build some kind of structure using string concatenation
as in fixed length file for output, and do not care to bother with data length checks or conversions.
otherwise you might as well use varchar() or nvarchar()
http://msdn.microsoft.com/en-us/library/ms143359(v=sql.100).aspx
In SQL Server 2005, trailing spaces specified in the first input parameter to the REPLACE function are trimmed when the parameter is of type char. For example, in the statement SELECT '<' + REPLACE(CONVERT(char(6), 'ABC '), ' ', 'L') + '>', the value 'ABC ' is incorrectly evaluated as 'ABC'.
In SQL Server 2008, trailing spaces are always preserved. For applications that rely on the previous behavior of the function, use the RTRIM function when specifying the first input parameter for the function. For example, the following syntax will reproduce the SQL Server 2005 behavior SELECT '<' + REPLACE(RTRIM(CONVERT(char(6), 'ABC ')), ' ', 'L') + '>'.

What is embedded SQL?

for example, Delphi has components TADOQuery, TADOStoredProcedure, TADOtable.
Do they represent Embedded SQL??
What is the difference between Embedded SQL and just using SQL in programming languages??
No, TADO* aren't embedded SQL.
Embedded SQL is a (mostly archaic) technique where you write specially delimited SQL in your code and use a pre-processing application to convert that to direct DB API calls.
You can read about it in Chapter 14 of the InterBase Embedded SQL guide [PDF], if you care to.
No those Delphi components are not embedded SQL. In embedded SQL the SQL statements are intermixed with regular code, the most common form I know of is ESQL/C. It should be considered deprecated.
The way it works is a preprocessor that translates all the SQL related stuff to the host language, and that pretranslated code is fed through the host language compiler.
A short fragment based on PostgreSQL's ESQL/C implementation:
void showesql() {
EXEC SQL BEGIN DECLARE SECTION;
int FooBar;
VARCHAR DooDad;
EXEC SQL END DECLARE SECTION;
EXEC SQL DECLARE foo_bar CURSOR FOR
SELECT number, ascii FROM foo
ORDER BY ascii;
EXEC SQL OPEN foo_bar;
EXEC SQL FETCH foo_bar INTO :FooBar, DooDad;
printf ("number is %d and ascii was %s\n",FooBar, DooDad);
EXEC SQL CLOSE foo_bar;
EXEC SQL COMMIT;
}
In my experience working in such code is quite hard, and that's why this once pretty common technique has been abandoned.
Embedded SQL is using SQL statements within your program code
http://en.wikipedia.org/wiki/Embedded_SQL