Remove SQL comments - sql

our stored procedures have developer comments and headers and as part of our deployment process we would like to remove these from the customer copy. Is there a method of achieving this within SQL Server 2005 or with another tool?

Don't know if it would suit, but you can use the WITH ENCRYPTION option to hide the entire contents. Do your end users need to see/modify any of the procedures?

I use an SQL tool called WinSQL (very handy, highly reccommended) that has an option to "Parse Comments Locally".
I don't use it much personally, but I have had it on accidentally when running my scripts that build my stored procs and it does clean them out of the proc source in the database. :-)
Even the free version has that option.

This option wasn't available when the question was asked, but in SQL 2012, we can now use SQL Server's own parser to help us out.
Removing Comments From SQL

A little late to the party but in case someone else stumbles across...
CREATE FUNCTION [usf_StripSQLComments] ( #CommentedSQLCode VARCHAR(max) )
RETURNS Varchar(max)
/****************************************************************************************
--######################################################################################
-- Mjwheele#yahoo.com -- Some count sheep. Some code. Some write code to count sheep.
--######################################################################################
--#############################################################################
-- Sample Call Script
--#############################################################################
Declare #SqlCode Varchar(Max)
Declare #objname varchar(max) = 'sp_myproc'
select #Sqlcode = OBJECT_DEFINITION(t.OBJECT_ID)
from sys.objects t
where t.name = #objname
select dbo.ssf_StripSQLComments( #Sqlcode )
****************************************************************************************/
AS
BEGIN
DECLARE #Sqlcode VARCHAR(MAX) =#CommentedSQLCode
Declare #i integer = 0
Declare #Char1 Char(1)
Declare #Char2 Char(1)
Declare #TrailingComment Char(1) = 'N'
Declare #UncommentedSQLCode varchar(Max)=''
Declare #Whackcounter Integer = 0
Declare #max Integer = DATALENGTH(#sqlcode)
While #i < #max
Begin
Select #Char1 = Substring(#Sqlcode,#i,1)
if #Char1 not in ('-', '/','''','*')
begin
if #Char1 = CHAR(13) or #Char1 = CHAR(10)
Select #TrailingComment = 'N'
Else if not (#Char1 = CHAR(32) or #Char1 = CHAR(9)) and #TrailingComment = 'N' -- Not Space or Tab
Select #TrailingComment = 'Y'
if #Whackcounter = 0
Select #UncommentedSQLCode += #Char1
select #i+=1
end
else
begin
Select #Char2 = #Char1
, #Char1 = Substring(#Sqlcode,#i+1,1)
If #Char1 = '-' and #Char2 = '-' and #Whackcounter = 0
Begin
While #i < #Max and Substring(#Sqlcode,#i,1) not in (char(13), char(10))
Select #i+=1
if Substring(#Sqlcode,#i,1) = char(13) and #TrailingComment = 'N'
Select #i+=1
if Substring(#Sqlcode,#i,1) = char(10) and #TrailingComment = 'N'
Select #i+=1
End
else If #Char1 = '*' and #Char2 = '/'
Begin
Select #Whackcounter += 1
, #i += 2
End
else If #Char1 = '/' and #Char2 = '*'
Begin
Select #Whackcounter -= 1
, #i += 2
End
else if #char2 = '''' and #Whackcounter = 0
begin
Select #UncommentedSQLCode += #char2
while Substring(#Sqlcode,#i,1) <> ''''
Begin
Select #UncommentedSQLCode += Substring(#Sqlcode,#i,1)
, #i +=1
end
Select #i +=1
, #Char1 = Substring(#Sqlcode,#i,1)
end
else
Begin
if #Whackcounter = 0
Select #UncommentedSQLCode += #Char2
Select #i+=1
end
end
End
Return #UncommentedSQLCode
END

You may want to check this out:
Remove Comments from SQL Server Stored Procedures.
Note: this doesn't handle comments that start with --, which SQL Server allows. Otherwise I would inquire into having a developer write a short filter app that reads the text in via a stream, and then remove the comments that way. Or write it yourself.

I ended up writing my own SQL comment remover in C#

I assume you save your procedure definitions to a text or .sql file that you then version control. You could always use something like notepadd++ to find/replace the strings you want then commit them as a production/customer tag. This is not elegant, but an option. I don't know of any third party tools and my google searches returned the same result as the other posters posted.

You can remove comments using regular expressions in C# like described here. It works for line comments, block comments, even when block comments are nested, and it can correctly identify and ignore comments delimiters when they are inside literals or bracketed named identifiers.

This a VB.NET code for removing SQL Coments
It's supposed the script is well formated syntaxicly under SQL Management Studio
Module Module1
Const TagBeginMultiComent = "/*"
Const TagEndMultiComent = "*/"
Const TagMonoComent = "--"
Public Fail As Integer
Function IsQuoteOpened(ByVal Value As String) As Boolean
Dim V As String = Replace(Value, "'", "")
If V Is Nothing Then Return 0
Return ((Value.Length - V.Length) / "'".Length) Mod 2 > 0
End Function
Function RemoveComents(ByVal Value As String) As String
Dim RetVal As String = ""
Dim Block As String
Dim Tampon As String
Dim NbComentIncluded As Integer = 0
Dim QuoteOpened As Boolean
Dim CommentOpen As Boolean
While Value.Length > 0
Tampon = ""
Block = ""
Dim P1 As Integer = InStr(Value, TagBeginMultiComent)
Dim P2 As Integer = InStr(Value, TagEndMultiComent)
Dim P3 As Integer = InStr(Value, TagMonoComent)
Dim Min As Integer
If P1 = 0 Then P1 = Value.Length + 1
If P2 = 0 Then P2 = Value.Length + 1
If P3 = 0 Then P3 = Value.Length + 1
Tampon = ""
If P1 + P2 + P3 > 0 Then
Min = Math.Min(P1, Math.Min(P2, P3))
Tampon = Left(Value, Min - 1)
Block = Mid(Value, Min, 2)
Value = Mid(Value, Min)
End If
If NbComentIncluded = 0 Then QuoteOpened = IsQuoteOpened(RetVal & Tampon)
If Not QuoteOpened Then
NbComentIncluded += -(Block = TagBeginMultiComent) + (Block = TagEndMultiComent)
If Block = TagMonoComent Then
Dim Ploc As Integer = InStr(Value, vbCrLf)
If Ploc = 0 Then
Value = ""
Else
Value = Mid(Value, Ploc - 2)
End If
End If
End If
If Not CommentOpen And NbComentIncluded = 0 Then
RetVal += Tampon
If ({TagBeginMultiComent, TagEndMultiComent, TagMonoComent}.Contains(Block) And QuoteOpened) Or
(Not {TagBeginMultiComent, TagEndMultiComent, TagMonoComent}.Contains(Block) And Not QuoteOpened) Then RetVal += Block
End If
CommentOpen = (NbComentIncluded > 0)
Value = Mid(Value, 3)
End While
Fail = -1 * (IsQuoteOpened(RetVal)) - 2 * (NbComentIncluded > 0)
If Fail <> 0 Then RetVal = ""
Return RetVal
End Function
Sub Main()
Dim InputFileName = "C:\Users\godef\OneDrive - sacd.fr\DEV\DelComentsSql\test.txt" '"C:\Users\sapgy01\OneDrive - sacd.fr\DEV\DelComentsSql\test.txt"
Dim Script As String = File.ReadAllText(InputFileName)
Dim InputDataArray As String() = Split(Script, vbCrLf)
Script = RemoveComents(Script)
If Fail Then
Console.WriteLine($"Fail : {Fail}")
If Fail And 1 = 1 Then Console.WriteLine("Toutes les quotes ne sont pas refermées")
If Fail And 2 = 2 Then Console.WriteLine("Tous les commentaires multiliqnes ne sont pas refermées")
Else
Console.WriteLine(Script)
End If
Console.ReadKey()
End Sub
End Module
Addon : a check is made for unclosed multilines coment and/or unclosed apostroph.
Example :
/* Commentaire principal
Suite du commentaire principal
/* Inclusion de commentaire
Suite du commentaire inclu
*/ suite commentaire principal
continuation commentaire principal
/* mono comentaire tagué multi lignes */
*/ select * from ref
-- mono commentaire
select ref.ref_lbl_code as 'code
de
la
-- ref --
' -- from ref as 'references' -- Fin de séquence
from ref as reference -- Mono commentaire fin de ligne
go -- lance l'exécution
select dbo.ref.REF_LBL_CODE as 'commentaire
/* Mulitlignes sur une ligne dans litteral */'
from ref as 'table_ref'
select ref.ref_lbl_code as 'commentaire
/* Mulitlignes
sur plusieurs lignes
dans litteral */'
from ref as '-- ref_table --'
-- Fin de l'exécution du ' -- script -- '

Related

Fuzzy logic matching

So, I'm looking at implementing Fuzzy logic matching in my company and having trouble getting good results. For starters, I'm trying to match up Company names with those on a list supplied by other companies.
My first attempt was to use soundex, but it looks like soundex only compares the first few sounds in the company name, so longer company names were too easily confused for one another.
I'm now working on my second attempt using the levenstein distance comparison. It looks promising, especially if I remove the punctuation first. However, I'm still having trouble finding duplicates without too many false positives.
One of the issues I have is companies such as widgetsco vs widgets inc. So, if I compare the substring of the length of the shorter name, I also pickup things like BBC University and CBC University campus. I suspect that a score using a combination of distance and longest common substring may be the solution.
Has anyone managed to build an algorithm that does such a matching with limited false positives?
We have had good results on name and address matching using a Metaphone function created by Lawrence Philips. It works in a similar way to Soundex, but creates a sound/consonant pattern for the whole value. You may find this useful in conjunction with some other techniques, especially if you can strip some of the fluff like 'co.' and 'inc.' as mentioned in other comments:
create function [dbo].[Metaphone](#str as nvarchar(70), #KeepNumeric as bit = 0)
returns nvarchar(25)
/*
Metaphone Algorithm
Created by Lawrence Philips.
Metaphone presented in article in "Computer Language" December 1990 issue.
*********** BEGIN METAPHONE RULES ***********
Lawrence Philips' RULES follow:
The 16 consonant sounds:
|--- ZERO represents "th"
|
B X S K J T F H L M N P R 0 W Y
Drop vowels
Exceptions:
Beginning of word: "ae-", "gn", "kn-", "pn-", "wr-" ----> drop first letter
Beginning of word: "wh-" ----> change to "w"
Beginning of word: "x" ----> change to "s"
Beginning of word: vowel or "H" + vowel ----> Keep it
Transformations:
B ----> B unless at the end of word after "m", as in "dumb", "McComb"
C ----> X (sh) if "-cia-" or "-ch-"
S if "-ci-", "-ce-", or "-cy-"
SILENT if "-sci-", "-sce-", or "-scy-"
K otherwise
K "-sch-"
D ----> J if in "-dge-", "-dgy-", or "-dgi-"
T otherwise
F ----> F
G ----> SILENT if "-gh-" and not at end or before a vowel
"-gn" or "-gned"
"-dge-" etc., as in above rule
J if "gi", "ge", "gy" if not double "gg"
K otherwise
H ----> SILENT if after vowel and no vowel follows
or "-ch-", "-sh-", "-ph-", "-th-", "-gh-"
H otherwise
J ----> J
K ----> SILENT if after "c"
K otherwise
L ----> L
M ----> M
N ----> N
P ----> F if before "h"
P otherwise
Q ----> K
R ----> R
S ----> X (sh) if "sh" or "-sio-" or "-sia-"
S otherwise
T ----> X (sh) if "-tia-" or "-tio-"
0 (th) if "th"
SILENT if "-tch-"
T otherwise
V ----> F
W ----> SILENT if not followed by a vowel
W if followed by a vowel
X ----> KS
Y ----> SILENT if not followed by a vowel
Y if followed by a vowel
Z ----> S
*/
as
begin
declare #Result varchar(25)
,#str3 char(3)
,#str2 char(2)
,#str1 char(1)
,#strp char(1)
,#strLen tinyint
,#cnt tinyint
set #strLen = len(#str)
set #cnt = 0
set #Result = ''
-- Preserve first 5 numeric values when required
if #KeepNumeric = 1
begin
set #Result = case when isnumeric(substring(#str,1,1)) = 1
then case when isnumeric(substring(#str,2,1)) = 1
then case when isnumeric(substring(#str,3,1)) = 1
then case when isnumeric(substring(#str,4,1)) = 1
then case when isnumeric(substring(#str,5,1)) = 1
then left(#str,5)
else left(#str,4)
end
else left(#str,3)
end
else left(#str,2)
end
else left(#str,1)
end
else ''
end
set #str = right(#str,len(#str)-len(#Result))
end
--Process beginning exceptions
set #str2 = left(#str,2)
if #str2 = 'wh'
begin
set #str = 'w' + right(#str , #strLen - 2)
set #strLen = #strLen - 1
end
else
if #str2 in('ae', 'gn', 'kn', 'pn', 'wr')
begin
set #str = right(#str , #strLen - 1)
set #strLen = #strLen - 1
end
set #str1 = left(#str,1)
if #str1 = 'x'
set #str = 's' + right(#str , #strLen - 1)
else
if #str1 in ('a','e','i','o','u')
begin
set #str = right(#str, #strLen - 1)
set #strLen = #strLen - 1
set #Result = #Result + #str1
end
while #cnt <= #strLen
begin
set #cnt = #cnt + 1
set #str1 = substring(#str,#cnt,1)
set #strp = case when #cnt <> 0
then substring(#str,(#cnt-1),1)
else ' '
end
-- Check if the current character is the same as the previous character.
-- If we are keeping numbers, only compare non-numeric characters.
if case when #KeepNumeric = 1 and #strp = #str1 and isnumeric(#str1) = 0 then 1
when #KeepNumeric = 0 and #strp = #str1 then 1
else 0
end = 1
continue -- Skip this loop
set #str2 = substring(#str,#cnt,2)
set #Result = case when #KeepNumeric = 1 and isnumeric(#str1) = 1
then #Result + #str1
when #str1 in('f','j','l','m','n','r')
then #Result + #str1
when #str1 = 'q'
then #Result + 'k'
when #str1 = 'v'
then #Result + 'f'
when #str1 = 'x'
then #Result + 'ks'
when #str1 = 'z'
then #Result + 's'
when #str1 = 'b'
then case when #cnt = #strLen
then case when substring(#str,(#cnt - 1),1) <> 'm'
then #Result + 'b'
else #Result
end
else #Result + 'b'
end
when #str1 = 'c'
then case when #str2 = 'ch' or substring(#str,#cnt,3) = 'cia'
then #Result + 'x'
else case when #str2 in('ci','ce','cy') and #strp <> 's'
then #Result + 's'
else #Result + 'k'
end
end
when #str1 = 'd'
then case when substring(#str,#cnt,3) in ('dge','dgy','dgi')
then #Result + 'j'
else #Result + 't'
end
when #str1 = 'g'
then case when substring(#str,(#cnt - 1),3) not in ('dge','dgy','dgi','dha','dhe','dhi','dho','dhu')
then case when #str2 in('gi', 'ge','gy')
then #Result + 'j'
else case when #str2 <> 'gn' or (#str2 <> 'gh' and #cnt+1 <> #strLen)
then #Result + 'k'
else #Result
end
end
else #Result
end
when #str1 = 'h'
then case when #strp not in ('a','e','i','o','u') and #str2 not in ('ha','he','hi','ho','hu')
then case when #strp not in ('c','s','p','t','g')
then #Result + 'h'
else #Result
end
else #Result
end
when #str1 = 'k'
then case when #strp <> 'c'
then #Result + 'k'
else #Result
end
when #str1 = 'p'
then case when #str2 = 'ph'
then #Result + 'f'
else #Result + 'p'
end
when #str1 = 's'
then case when substring(#str,#cnt,3) in ('sia','sio') or #str2 = 'sh'
then #Result + 'x'
else #Result + 's'
end
when #str1 = 't'
then case when substring(#str,#cnt,3) in ('tia','tio')
then #Result + 'x'
else case when #str2 = 'th'
then #Result + '0'
else case when substring(#str,#cnt,3) <> 'tch'
then #Result + 't'
else #Result
end
end
end
when #str1 = 'w'
then case when #str2 not in('wa','we','wi','wo','wu')
then #Result + 'w'
else #Result
end
when #str1 = 'y'
then case when #str2 not in('ya','ye','yi','yo','yu')
then #Result + 'y'
else #Result
end
else #Result
end
end
return #Result
end
You want to use something like Levenshtein Distance or another string comparison algorithm. You may want to take a look at this project on Codeplex.
http://fuzzystring.codeplex.com/
Are you using Access? If so, consider the '*' character, without the quotes. If you're using SQL Server, use the '%' character. However, this really isn't fuzzy logic, it's really the Like operator. If you really need fuzzy logic, export your data-set to Excel and load the AddIn from the URL below.
https://www.microsoft.com/en-us/download/details.aspx?id=15011
Read the instructions very carefully. It definitely works, and it works great, but you need to follow the instructions, and it's not completely intuitive. The first time I tried it, I didn't follow the instructions, and I wasted a lot of time trying to get it to work. Eventually I figured it out, and it worked great!!
I found success implementing a function I found here on Stack Overflow that would find the percentage of strings that match. You can then adjust tolerance till you get an appropriate amount of matches/mismatches. The function implementation will be listed below, but the gist is including something like this in your query.
DECLARE #tolerance DEC(18, 2) = 50;
WHERE dbo.GetPercentageOfTwoStringMatching(first_table.name, second_table.name) > #tolerance
Credit for the following percent matching function goes to Dragos Durlut, Dec 15 '11.
The credit for the LEVENSHTEIN function was included in the code by Dragos Durlut.
T-SQL Get percentage of character match of 2 strings
CREATE FUNCTION [dbo].[GetPercentageOfTwoStringMatching]
(
#string1 NVARCHAR(100)
,#string2 NVARCHAR(100)
)
RETURNS INT
AS
BEGIN
DECLARE #levenShteinNumber INT
DECLARE #string1Length INT = LEN(#string1)
, #string2Length INT = LEN(#string2)
DECLARE #maxLengthNumber INT = CASE WHEN #string1Length > #string2Length THEN #string1Length ELSE #string2Length END
SELECT #levenShteinNumber = [dbo].[LEVENSHTEIN] ( #string1 ,#string2)
DECLARE #percentageOfBadCharacters INT = #levenShteinNumber * 100 / #maxLengthNumber
DECLARE #percentageOfGoodCharacters INT = 100 - #percentageOfBadCharacters
-- Return the result of the function
RETURN #percentageOfGoodCharacters
END
-- =============================================
-- Create date: 2011.12.14
-- Description: http://blog.sendreallybigfiles.com/2009/06/improved-t-sql-levenshtein-distance.html
-- =============================================
CREATE FUNCTION [dbo].[LEVENSHTEIN](#left VARCHAR(100),
#right VARCHAR(100))
returns INT
AS
BEGIN
DECLARE #difference INT,
#lenRight INT,
#lenLeft INT,
#leftIndex INT,
#rightIndex INT,
#left_char CHAR(1),
#right_char CHAR(1),
#compareLength INT
SET #lenLeft = LEN(#left)
SET #lenRight = LEN(#right)
SET #difference = 0
IF #lenLeft = 0
BEGIN
SET #difference = #lenRight
GOTO done
END
IF #lenRight = 0
BEGIN
SET #difference = #lenLeft
GOTO done
END
GOTO comparison
COMPARISON:
IF ( #lenLeft >= #lenRight )
SET #compareLength = #lenLeft
ELSE
SET #compareLength = #lenRight
SET #rightIndex = 1
SET #leftIndex = 1
WHILE #leftIndex <= #compareLength
BEGIN
SET #left_char = substring(#left, #leftIndex, 1)
SET #right_char = substring(#right, #rightIndex, 1)
IF #left_char <> #right_char
BEGIN -- Would an insertion make them re-align?
IF( #left_char = substring(#right, #rightIndex + 1, 1) )
SET #rightIndex = #rightIndex + 1
-- Would an deletion make them re-align?
ELSE IF( substring(#left, #leftIndex + 1, 1) = #right_char )
SET #leftIndex = #leftIndex + 1
SET #difference = #difference + 1
END
SET #leftIndex = #leftIndex + 1
SET #rightIndex = #rightIndex + 1
END
GOTO done
DONE:
RETURN #difference
END
Note: If you need to compare two or more fields (which I don't think you do) you can add another call to the function in the WHERE clause with a minimum tolerance. I also found success averaging the percentMatching and comparing it against a tolerance.
DECLARE #tolerance DEC(18, 2) = 25;
--could have multiple different tolerances for each field (weighting some fields as more important to be matching)
DECLARE #avg_tolerance DEC(18, 2) = 50;
WHERE AND dbo.GetPercentageOfTwoStringMatching(first_table.name, second_table.name) > #tolerance
AND dbo.GetPercentageOfTwoStringMatching(first_table.address, second_table.address) > #tolerance
AND (dbo.GetPercentageOfTwoStringMatching(first_table.name, second_table.name)
+ dbo.GetPercentageOfTwoStringMatching(first_table.address, second_table.address)
) / 2 > #avg_tolerance
The benefit of this solution is the tolerance variables can be specific per field (weighting the importance of certain fields matching) and the average can insure general matching across all fields.
Firstly, I suggest, you make sure that you can't match on any other attribute and company names are all you have(because fuzzy matching is bound to give you some false positives). If you want to go ahead with fuzzy matching you could use the following steps:
Remove all stop words from the text. For example : Co, Inc etc.
If your database is very large, make use of an indexing method such as blocking or sorted neighbourhood indexing.
Finally compute the fuzzy score using the Levenshtein distance. You could use the token_set_ratio or partial_ratio functions in Fuzzywuzzy.
Also, I found the following video which aims to solve the same problem: https://www.youtube.com/watch?v=NRAqIjXaZvw
The Nanonets blog also contains several resources on the subject that could potentially be helpful.

Uniqueidentifier as parameter in SQL Server Function

I have created a Function in SQL Server 2012 that I will use in a Check Constraint on a table.
The function works as expected if I do:
SELECT [dbo].[CheckValidCardnumberForShellTankingen] ('700678036658047691' ,'2925CA00-6DD5-4F9D-AB0E-AA15DBBD388B')
But when I try to set the expression in Check Constraint so:
([dbo].[CheckValidCardnumberForShellTankingen]([Volledig kaartnummer],[RollBackCode])=(1))
I get a Messaage: "Error validating constraint 'CK_MyConstraint'"
I use the Uniqueidentifier in a Where clause and the strange thing is if I replace the parameter with string containing the Uniqueidentifier I dont get this error.
Here is the Function:
-- =============================================
-- Author: Anders Pedersen
-- Create date: 2015-02-13
-- Description: Check of the Cardnumber of a transaction is valid.
-- =============================================
CREATE FUNCTION [dbo].[CheckValidCardnumberForShellTankingen]
(
-- Add the parameters for the function here
#Cardnumber NvarChar(50),
#RollBackCode NvarChar(200)
)
RETURNS BIT
AS
BEGIN
-- Declare the return variable here
DECLARE
#Result BIT
,#ResultLenght BIT
,#ResultPrefix BIT
,#CardLenght INT
,#SupplierID INT
,#UseCardnumber BIT
,#Prefix NvarChar(50)
-- Add the T-SQL statements to compute the return value here
SET #Result = 0
SET #ResultLenght = 0
SET #ResultPrefix = 0
SET #CardLenght = -1
SET #SupplierID = -1
SET #UseCardnumber = 0
SET #Prefix = ''
-- Get the UseCardnumber and the SupplierID
SELECT #UseCardnumber = C.UseCardNumber, #SupplierID = F.SupplierID
FROM Client C INNER JOIN
ClientFileUploads F ON C.ClientID = F.ClientID
WHERE F.RollBackCode = #RollBackCode
--WHERE F.RollBackCode = '2925CA00-6DD5-4F9D-AB0E-AA15DBBD388B'
-- Only carry out the check if the Client use Cards else set the check to True (1)
IF #UseCardnumber = 1
BEGIN
SELECT #CardLenght = [CardNumberLenght], #Prefix = ISNULL([Prefix],'') FROM [dbo].[Supplier] AS S WHERE S.SupplierID = #SupplierID
IF (#CardLenght IS NULL) OR (#CardLenght = 0)
BEGIN
SET #ResultLenght = 1
END
ELSE
BEGIN
IF (LEN(#Cardnumber) - #CardLenght)= 0
BEGIN
SET #ResultLenght = 1
END
ELSE
BEGIN
SET #ResultLenght = 0
END
END
IF SUBSTRING(#Cardnumber, 1, LEN(#Prefix)) = #Prefix
BEGIN
SET #ResultPrefix = 1
END
ELSE
BEGIN
SET #ResultPrefix = 0
END
IF ((#ResultLenght = 1) AND (#ResultPrefix = 1))
BEGIN
SET #Result = 1
END
ELSE
BEGIN
SET #Result = 0
END
END
ELSE
BEGIN
SET #Result = 1
END
-- Return the result of the function
RETURN #Result
END
GO
If #RollBackCode is a uniqueidentifier, I recommend making the parameter a uniqueidentifier and not a varchar.
As Rhys Jones points out, you shouldn't use a UDF in a check constraint.
See
https://dba.stackexchange.com/questions/22297/udf-in-check-constraint-downside
https://social.msdn.microsoft.com/Forums/sqlserver/en-US/078b720f-faac-425c-b51a-33bcecb263d2/check-constraint-with-udf-problem-with-lots-of-data?forum=transactsql
http://sqlblog.com/blogs/tibor_karaszi/archive/2009/12/17/be-careful-with-constraints-calling-udfs.aspx
If you need to check in a trigger and roll back -- SQL Server - After Insert/ For Insert - Rollback

HEX TO RAW function for DB2 on z/OS

I need to cast an INT value as HEX (not to hex).
For example, given the value 1234, I want to transform it to x'1234'.
My first inclination was to use the hex function, but that does not produce the desired results:
hex(1234) = x'04D2'
I need a function or algorithm such that
my_function(1234) = x'1234'
EDIT: Thanks to Lennart's answer I learned that it would be the equivalent of HEXTORAW or
VARCHAR_BIT_FORMAT which exist on DB2 for LUW, but not for z/OS
Not sure I understand your question, is this in the ballpark?
with t (s) as (values ('1234'),(x'F0F0F0F0F0F0F0F0F0F0F0F0F0'))
select s
, case when translate(s, '', '0123456789') = ''
then hextoraw(s)
else s
end
from t;
S 2
------------- --------------------------
1234 x'1234'
ððððððððððððð ððððððððððððð
It is possible to do this transformation using the EBCDIC_CHR function. This solution assumes your system character encoding is EBCDIC.
See this thread for discussion, and UDF: http://www.idug.org/p/fo/st/thread=43924
This user defined function is from that thread. It will receive a varchar input and for each pair of values convert them to raw using EBCDIC_CHR, concatenating it all together.
CREATE FUNCTION UDFUNC.HEX2RAW(INSTR VARCHAR(1024))
RETURNS VARCHAR(2048)
DETERMINISTIC
NO EXTERNAL ACTION
CONTAINS SQL
BEGIN
DECLARE invalid_hexval CONDITION FOR SQLSTATE '22007';
DECLARE VALIDSTR VARCHAR(1024) default '';
DECLARE OUTSTR VARCHAR(2048) DEFAULT '';
DECLARE HEXCHR CHAR(16) DEFAULT '0123456789ABCDEF';
DECLARE MODVAL INT DEFAULT 0;
DECLARE LENSTR INT;
DECLARE I INT DEFAULT 0;
DECLARE J INT DEFAULT 0;
IF INSTR IS NULL THEN
RETURN NULL;
END IF;
set VALIDSTR = TRANSLATE(INSTR,'',HEXCHR);
IF (VALIDSTR <> '') THEN
SIGNAL invalid_hexval SET MESSAGE_TEXT = 'Not Hex: [' || CAST(INSTR AS VARCHAR(59))||']';
END IF;
SET MODVAL = MOD(LENGTH(INSTR),2);
IF MODVAL <> 0 THEN
SET INSTR = CONCAT('0',INSTR);
END IF;
SET LENSTR = LENGTH(INSTR);
WHILE I < LENSTR DO
SET J = 16 * (POSSTR(HEXCHR,SUBSTR(INSTR,I+1,1))-1);
SET I = I + 1;
SET J = J + (POSSTR(HEXCHR,SUBSTR(INSTR,I+1,1))-1);
SET I = I + 1;
SET OUTSTR = CONCAT(OUTSTR,EBCDIC_CHR(J));
END WHILE;
RETURN OUTSTR;
END#
Function usage:
SELECT UDFUNC.HEX2RAW('1234')
, HEX(UDFUNC.HEX2RAW('1234'))
, UDFUNC.HEX2RAW('1234567')
, HEX(UDFUNC.HEX2RAW('1234567'))
FROM SYSIBM.SYSDUMMY1;
results in:
1 2 3 4
-- ---- ---- --------
1234 áÅ 01234567

How to determine whether the number is float or integer

I need to write this query in SQL Server:
IF isFloat(#value) = 1
BEGIN
PRINT 'this is float number'
END
ELSE
BEGIN
PRINT 'this is integer number'
END
Please help me out with this, thanks.
declare #value float = 1
IF FLOOR(#value) <> CEILING(#value)
BEGIN
PRINT 'this is float number'
END
ELSE
BEGIN
PRINT 'this is integer number'
END
Martin, under certain circumstances your solution gives an incorrect result if you encounter a value of 1234.0, for example. Your code determines that 1234.0 is an integer, which is incorrect.
This is a more accurate snippet:
if cast(cast(123456.0 as integer) as varchar(255)) <> cast(123456.0 as varchar(255))
begin
print 'non integer'
end
else
begin
print 'integer'
end
Regards,
Nico
DECLARE #value FLOAT = 1.50
IF CONVERT(int, #value) - #value <> 0
BEGIN
PRINT 'this is float number'
END
ELSE
BEGIN
PRINT 'this is integer number'
END
See whether the below code will help. In the below values only 9,
2147483647, 1234567 are eligible as Integer. We can create this as
function and can use this.
CREATE TABLE MY_TABLE(MY_FIELD VARCHAR(50))
INSERT INTO MY_TABLE
VALUES('9.123'),('1234567'),('9'),('2147483647'),('2147483647.01'),('2147483648'), ('2147483648ABCD'),('214,7483,648')
SELECT *
FROM MY_TABLE
WHERE CHARINDEX('.',MY_FIELD) = 0 AND CHARINDEX(',',MY_FIELD) = 0
AND ISNUMERIC(MY_FIELD) = 1 AND CONVERT(FLOAT,MY_FIELD) / 2147483647 <= 1
DROP TABLE MY_TABLE
OR
DECLARE #num VARCHAR(100)
SET #num = '2147483648AS'
IF ISNUMERIC(#num) = 1 AND #num NOT LIKE '%.%' AND #num NOT LIKE '%,%'
BEGIN
IF CONVERT(FLOAT,#num) / 2147483647 <= 1
PRINT 'INTEGER'
ELSE
PRINT 'NOT-INTEGER'
END
ELSE
PRINT 'NOT-INTEGER'

Generate a receipt number in this range

How would I go about generating a unique receipt number in the following range:
GA00000-GZ99999? I am not allowed to use the 'I' and 'O' letters so GI00000-GI99999 & GO00000-GO99999 would be excluded.
Ideally, I'd like to create this in T-SQL but can also do it in VB.Net. This number will be stored in SQL and I can access it prior to generating the next one. They do not have to be sequential.
Thanks.
Create an array of all characters that are permitted as the first digit.
For any numeric receipt id n the receipt code will be G + array[n/1000000] + n % 1000000.
keep a suffix pointer. when your index wraps over 99999, increment it. Check for your special cases and exhaustion (e.g. Z).
Here's a VB.NET version to get the next sequential number, given the previous one. I think I covered all the cases.
Private Const FirstReceiptNumber As String = "GA00000"
Public Function GenerateReceiptNumber(ByVal lastNumber As String) As String
If lastNumber.Length <> 7 Then
Throw New ArgumentException("Incorrect length", "lastNumber")
End If
If lastNumber.StartsWith("G") = False Then
Throw New ArgumentException("Incorrect start character", "lastNumber")
End If
Dim letterPortion As Char = lastNumber.Chars(1)
If letterPortion < "A"c Or letterPortion > "Z"c Then
Throw New ArgumentException("Non-letter second character", "lastNumber")
End If
If letterPortion = "I"c Or letterPortion = "O"c Then
Throw New ArgumentException("Invalid second character", "lastNumber")
End If
Dim numericPortionString As String = lastNumber.Substring(2)
Dim numericPortion As Integer
If Integer.TryParse(numericPortionString, numericPortion) = False Then
Throw New ArgumentException("Invalid numeric portion", "lastNumber")
End If
If numericPortion = 99999 Then
If letterPortion = "Z"c Then
Throw New ArgumentException("No more receipt numbers possible", "lastNumber")
End If
numericPortion = 0
letterPortion = letterPortion + Chr(1)
If letterPortion = "I"c Or letterPortion = "O"c Then
letterPortion = letterPortion + Chr(1)
End If
Else
numericPortion = numericPortion + 1
End If
Return String.Format("G{0}{1:00000}", letterPortion, numericPortion)
End Function
I decided to do it as the following:
CREATE FUNCTION [dbo].[fn_generateReceiptNumber]()
RETURNS NCHAR(7)
AS
BEGIN
-- Declare the return variable here
DECLARE #result NCHAR(7);
DECLARE #prefix NCHAR(1);
DECLARE #suffix INT;
DECLARE #currentMax NCHAR(7);
SELECT #currentMax = MAX(ISNULL(fp.CustomReceiptNo, 'GA00001')) FROM dbo.FinPayment fp;
SELECT #prefix = SUBSTRING(#currentMax,2,1);
SELECT #suffix = CAST(SUBSTRING(#currentMax,3,7) AS INT);
IF((#suffix + 1) > 99999)
BEGIN
SELECT #suffix = 0;
END
ELSE
BEGIN
SELECT #suffix = #suffix + 1;
END
IF(#suffix = 0)
BEGIN
IF(#prefix = 'Z')
BEGIN
RETURN -1;
END
ELSE
BEGIN
IF(NCHAR(UNICODE(#prefix)+1) IN ('I', 'O'))
BEGIN
SELECT #prefix = NCHAR(UNICODE(#prefix)+2);
END
ELSE
BEGIN
SELECT #prefix = NCHAR(UNICODE(#prefix)+1);
END
END
END
-- Return the result of the function
SELECT #result = NCHAR(71) + #prefix + CAST(RIGHT('00000' + RTRIM(#suffix), 5) AS NCHAR(5));
RETURN #result;
END
GO
Thank you everyone for the input.
Steve.