I need some help understand this line of T-SQL code - tsql

I read this line of code in a book but could not fully comprehend its application. I understand this is regarding implicit conversion but don't fully understand the purpose of some of the code.
DECLARE #sql NVARCHAR(
SELECT ISNULL(''5'', 5),
ISNULL(5, ''5''),
COALESCE(''5'', 5),
COALESCE(5, ''5'') ;
' ;
EXEC sp_executesql #sql ;
SELECT column_ordinal,
is_nullable,
system_type_name
FROM master.sys.dm_exec_describe_first_result_set(#sql, NULL, 0) a ;

I'll go out on a limb and guess that it was intended to be executable code that demonstrates the difference between two similar TSQL functions. This example should produce the expected results:
SELECT column_ordinal, is_nullable, system_type_name FROM sys.dm_exec_describe_first_result_set
( N'SELECT ISNULL(''5'', 5), ISNULL(5, ''5''), COALESCE(''5'', 5), COALESCE(5, ''5'');', null, 0);
A SQL Fiddle example is available here.
A careful reading of the documentation for IsNull() and Coalesce(), paying particular attention to the Return Types of each, will explain the results. Data Type Precedence is also illustrative.

Related

Approaches to execute PostgreSQL's concat() instead of || in JOOQ?

The ||-operator and the concat(...)-function in PostgreSQL behave differently.
select 'ABC'||NULL||'def';
-- Result: NULL
select concat('ABC', NULL, 'def');
-- Result: 'ABCdef'
concat(...) ignores NULL values, but a NULL whithin a || expression makes the whole result become NULL.
In JOOQ, the DSL.concat() in the PostgreSQL dialect renders expressions using the ||-operator:
Java: dsl.select(
DSL.concat(
DSL.inline("ABC"),
DSL.inline(null, SQLDataType.VARCHAR),
DSL.inline("def"))
).execute();
SQL: select ('ABC' || null || 'def')
Result: NULL
I am looking for (elegant?) ways to invoke the concat(...)-function instead of the ||-operator via JOOQ in PostgreSQL:
Java: dsl.select(???).execute();
SQL: select concat('ABC', null, 'def')
Result: 'ABCdef'
I found two ways to achieve the posed objective.
Approach #1:
dsl.select(
field(
"concat({0})",
SQLDataType.VARCHAR,
list(
inline("ABC"),
inline(null, SQLDataType.VARCHAR),
inline("def")
)
)
).execute();
This has to the intended behavior, but necessitates the in my eyes ugly "concat({0})". A more elegant approach from my point of view is:
Approach #2:
dsl.select(
function(
"concat",
SQLDataType.VARCHAR,
inline("ABC"),
inline(null, SQLDataType.VARCHAR),
inline("def")
)
).execute();
This solution does not involve inline SQL with placeholders as approach #1. Why JOOQ generates || instead of concat(...) in the first place is still to be expounded, though.

SQL: How to get a string located between two specific characters in a given string

I'm sending a file location to my stored procedure.
There might be two scenarios:
1. \\filedirectory\Department\Project\2000-01-12\
2. \\filedirectory\Departments\Project\20000112\
I'm trying to find a way to get a string between 2 last slashes and convert it into the MMDDYY format.
How do I do that?
I may have gotten carried away but this works.
DECLARE #path VARCHAR(MAX) = '\\filedirectory\Department\Project\2000-01-12\'
SELECT REPLACE(CONVERT(CHAR(10),CAST(REPLACE(REVERSE(SUBSTRING(REVERSE(#path), 1, CHARINDEX('\', REVERSE(#path), 2))), '\', '') AS DATE), 1), '/', '')
Another way...
DECLARE #path VARCHAR(MAX) = '\\filedirectory\Department\Project\2000-01-12\'
select
replace(convert(varchar,cast(replace(right(#path,charindex('\',reverse(#path),2)-1),'\','') as date),1),'/','')

Idempotent QUOTENAME()?

The QUOTENAME() function in tsql is oft used when building dynamic sql to handle "special" charatcers within names. If one winds up with redundant calls via nested code it falls apart.
declare #name sysname = 'simple_name'
print '#name: ' + #name
print 'QUOTENAME(#name): ' + QUOTENAME(#name)
print 'QUOTENAME(QUOTENAME(#name)): ' + QUOTENAME(QUOTENAME(#name))
The results
#name: simple_name
QUOTENAME(#name): [simple_name]
QUOTENAME(QUOTENAME(#name)): [[simple_name]]]
Other than making sure these sort of chained calls are impossible within a codebase, has anyone a idempotent version of that is safe even for nested calls?
I don't know regex very well, but I'd imagine you could do some kind of #sql scrub before the exec.
I'm sure some kind of regex would be quicker, but here is my test.
DECLARE #name sysname = 'simple_name'
DECLARE #SQLTest nvarchar(max)
SET #SQLTest = 'select QUOTENAME(QUOTENAME(#name)), ''blah'', ''blah'''
SET #SQLTest = Replace(#SQLTest, Substring(#SQLTest, PatIndex('%QUOTENAME(QUOTENAME(%))%', #SQLTest), 20), 'QUOTENAME(')
SET #SQLTest = Replace(#SQLTest, Substring(#SQLTest, PatIndex('%))%', #SQLTest), 2), ')')
--- this might be a safer second replace?
SET #SQLTest = Replace(#SQLTest, Substring(reverse(#SQLTest), PatIndex('%))%EMANETOUQ%', reverse(#SQLTest)), 2), ')')
-
select #SQLTest
exec sp_executesql #SQLTest, N'#name sysname', #name

Unable to use variable in DATEADD(#pinterval, #pinterval, #DayPlus)

DECLARE #pinterval INT = 1, #DayPlus DATETIME = '2016-07-01', #datepart VARCHAR(20) = 'MONTH'
SET #DayPlus = DATEADD(#datepart, #pinterval, #DayPlus)
SELECT #DayPlus
Do we have any alternative to accomplish task ? I have to do it in loop so I can't define it every time based on interval value. Only date part is not acceptable as variable because if I use as
DATEADD(MONTH, #pinterval, #DayPlus)
then it's working fine. Not sure but I can understand the issue but I am seeking for the quick solution. I have visited the web but didn't get exact solution.
It's not possible to substitute a variable for the first argument to DATEADD1, since what is wanted here is a name, not a string.
About the best you can do is a CASE expression:
SET #DayPlus =
CASE #datepart
WHEN 'MONTH' THEN DATEADD(MONTH, #pinterval, #DayPlus)
WHEN 'YEAR' THEN DATEADD(YEAR, #pinterval, #DayPlus)
WHEN 'DAY' THEN DATEADD(DAY, #pinterval, #DayPlus)
--TODO - add all parts you might wish to use
END
1This is even stated in the documentation:
User-defined variable equivalents are not valid.
if this is only the way then I have to repeat this for more than 10times
No, use CROSS APPLY
#Shnugo it sounds good, can you please help me by placing the complete code as an answer. Please !!
About CROSS APPLY: generate something like a variable dynamically
CREATE TABLE dbo.Test(ID INT, SomeDate DATE);
GO
INSERT INTO dbo.Test VALUES(1,{d'2017-01-01'}),(2,{d'2017-02-02'})
GO
DECLARE #Intervall VARCHAR(100)='DAY';
DECLARE #Count INT=1
SELECT dbo.Test.*
,t.AddedDate
FROM dbo.Test
CROSS APPLY(SELECT CASE #Intervall WHEN 'MONTH' THEN DATEADD(MONTH,#Count,SomeDate)
WHEN 'DAY' THEN DATEADD(DAY,#Count,SomeDate)
ELSE SomeDate END AS AddedDate) AS t;
GO
DROP TABLE dbo.Test;
You could use dynamic SQL:
DECLARE #pinterval INT = 1,
#DayPlus DATETIME = '2016-07-01',
#datepart NVARCHAR(20) = 'MONTH'
DECLARE #sql NVARCHAR(MAX) = N'SET #DayPlus = DATEADD('+#datepart+', #pinterval, #DayPlus)',
#params NVARCHAR(MAX) = N'#DayPlus DATETIME OUTPUT, #pinterval INT'
EXEC sp_executesql #sql, #params, #pinterval = #pinterval, #DayPlus = #DayPlus OUTPUT
SELECT #DayPlus
sp_executesql is used for 2 reasons:
to minimize SQL injections risk, it will not eliminate it because of #datepart
it will make sure your dynamic query's plan is going to be cached - which might be beneficial with simple queries (where query time vs compilation time matters)

Adapt T-SQL code to PL/SQL code

I just have a problem to convert some T-SQL code into PL/SQL.
This is the original part of code :
SET #ISOweek= DATEPART(wk,#DATE)+1
-DATEPART(wk,CAST(DATEPART(yy,#DATE) as CHAR(4))+'0104')
And after many research, i modify like that :
SET #ISOweek = TO_NUMBER(TRUNC(#DATE, 'IW')) + 1
- TO_NUMBER(TRUNC( (TO_CHAR(TRUNC(#DATE, 'YYYY'))) + '0104', 'IW'))
I can't test my code, that's why i come here for obtain your help. Can you say me if it's correct, or not? And what i've to modify because i'm a lost...
I read a lot of articles on this website, always helpfull.
Thank's for your answers
Cordialy,
Guillaume
The first problem it's ok, i've got a lot of responses and thnak's ;).
But i've another problem :
My T-SQL script :
USE [myBD ]
GO
SET ANSI_NULLS ON
GO
SET QUOTED_IDENTIFIER ON
GO
create function [Entity].[LPAD]
(
#pad_value varchar(500),
#pad_length int,
#pad_with varchar(10) = ' '
)
returns varchar(5000)
as
BEGIN
return ( replace(str(#pad_value,#pad_length),' ',#pad_with) )
END
GO
And i convert it in PL/SQL :
create OR REPLACE FUNCTION myBDOrcl.LPAD
(
pad_value varchar2,
pad_length number,
pad_with varchar2 := ' '
)
return varchar2
is retour varchar2(5000);
BEGIN
retour := replace(TO_CHAR(pad_length, pad_value ), ' ', pad_with);
return retour;
END;
I play the script and it's ok. BUT, i don't know how can I modify my variables : pad_value_varchar2 and the others, to specify the size...
I want to place a declare block, but it's not working....
Thank's for help guys