Assistance req translating VBA into SQL User Defined Function

mdlueck

Sr. Application Developer
Local time
Today, 17:49
Joined
Jun 23, 2011
Messages
2,648
Greetings,

I would like to teach SQL Server via an UDF how to decode/encode my Revision code storage scheme. There are times when it would be helpful if SQL Server could perform the translation rather than rely on VBA code to perform the operation on each workstation.

The conversion involves encoding revision codes 1-99 followed by A-ZZ into a numeric datatype so that revisions will sort correctly, aka revision B is higher than revision A or revision 2.

Original working VBA code:
Code:
Public Function revconv_ConvA2N(ByVal strInput As String) As Integer
On Error GoTo Err_revconv_ConvA2N

  Dim lLen As Long
  Dim intN As Integer
  Dim intR As Integer

  lLen = Len(strInput)

  If IsNumeric(strInput) Then
    If (lLen = 1) Or (lLen = 2) Then
      revconv_ConvA2N = Int(strInput)
    Else
      revconv_ConvA2N = -1
    End If
  Else
    Select Case lLen
      Case 0
        revconv_ConvA2N = -1
      Case 1
        revconv_ConvA2N = 99 + (Asc(strInput) - 64)
      Case 2
        intN = 26 * (Asc(Mid(strInput, 1, 1)) - 64)
        intR = Asc(Mid(strInput, 2, 1)) - 64
        revconv_ConvA2N = 99 + (Int(Trim(Str(intN + intR))))
      Case Else
        revconv_ConvA2N = -1
    End Select
  End If

Exit_revconv_ConvA2N:
  Exit Function

Err_revconv_ConvA2N:
  Call errorhandler_MsgBox("Module: modshared_revconv, Function: revconv_ConvA2N()")
  revconv_ConvA2N = 0
  Resume Exit_revconv_ConvA2N

End Function
As far as I have gotten with translating it to SQL. I found that the SQL CASE operates very differently than VBA's CASE.
Code:
-- Define name of USER-DEFINED FUNCTION for this script
:setvar UDFNAME frevconv_ConvA2N
USE [Fandango]
GO
SET ANSI_NULLS ON
GO
SET QUOTED_IDENTIFIER ON
GO
-- Check if the USER-DEFINED FUNCTION exists already, DROP if found
IF EXISTS(SELECT * FROM [sys].[objects] WHERE [type_desc] = 'SQL_SCALAR_FUNCTION' AND [name] = '$(UDFNAME)')
  DROP FUNCTION [dbo].[$(UDFNAME)]
GO
CREATE FUNCTION [dbo].[$(UDFNAME)] (
  -- Add the parameters for the user-defined function here
  @strInput AS varchar(2)
  )
RETURNS smallint
AS
BEGIN
-- Insert statements for user-defined function here
  DECLARE @intLen integer
  DECLARE @sintN smallint
  DECLARE @sintR smallint

  @intLen = Len(@strInput)

  IF IsNumeric(@strInput) THEN
    IF (@intLen = 1) Or (@intLen = 2) THEN
      RETURN = SmallInt(@strInput)
    ELSE
      RETURN = -1
    END IF
  ELSE
    @intLen = Len(@strInput)
  
    SELECT CASE @intLen
      CASE 0
        RETURN = -1
      CASE 1
        RETURN = 99 + (Asc(@strInput) - 64)
      CASE 2
        intN = 26 * (Asc(Substring(@strInput, 1, 1)) - 64)
        intR = Asc(Substring(@strInput, 2, 1)) - 64
        RETURN = 99 + (SmallInt(fTRIM(Str(intN + intR))))
      CASE ELSE
        RETURN = -1
    END SELECT
  END IF

END

Anyone with suggestions how to successfully accomplish the VBA to SQL translation? TIA!
 
I don't think there will be a much better way to do this, as it's the logic that's complex rather than the syntax as such.

Are you using your Function with cross apply when you call it? otherwise it will do each row in the select one by one and could have a negative effect on performance
 
I don't think there will be a much better way to do this, as it's the logic that's complex rather than the syntax as such.

However SQL's CASE does not appear to be able to support multiple LOC's within each CASE, as VBA's does.

SQL's CASE is merely to select the correct value, not perform logic.

So I believe I need another solution to mimic VBA's CASE.

Are you using your Function with cross apply when you call it? otherwise it will do each row in the select one by one and could have a negative effect on performance

The way I am intending to use it is in Stored Procedures which select records. The SELECT will include both the Revision code column (not decoded) and also the decoded Revision value... so pass the selected Revision code through the decode UDF and include that resulting value in the Recordset.

There are times I need SQL Server to be able to calculate the Revision value without relying on VBA to do the decoding.
 
SELECT CASE 2

And yes, I see that I could make it one overally complicated LOC by further function embedding at the cost to readability.

I was preferring to ask if there was another to do a VBA-like SELECT in SQL so that I could maintain code read-ability.
 
All right, this question just moved from "test tube" to "needed for production". Specifically this "Base 36 > Base 10" / "Base 10 > Base 36" code is now needed in production in a SQL UDF version.

The way I see it, CASE in SQL is suppose to only arrive at a single value which is placed into a single SQL Host Variable / Column Name.

In VBA, you may execute multiple LOC's within the context of each CASE.

Q: Would a SQL IF statement be able to more closely mimic a VBA CASE statement? I need to execute multiple LOC's as result of the evaluation of a logical criteria.

Any other conditional branching syntax in SQL which I have not considered yet on this thread?

TIA!
 
Perhaps getting close now... down to a single complaint:

Code:
-- Define name of USER-DEFINED FUNCTION for this script
:setvar UDFNAME frevconv_ConvA2N
USE [Fandango]
GO
SET ANSI_NULLS ON
GO
SET QUOTED_IDENTIFIER ON
GO
-- Check if the USER-DEFINED FUNCTION exists already, DROP if found
IF EXISTS(SELECT * FROM [sys].[objects] WHERE [type_desc] = 'SQL_SCALAR_FUNCTION' AND [name] = '$(UDFNAME)')
  DROP FUNCTION [dbo].[$(UDFNAME)]
GO
CREATE FUNCTION [dbo].[$(UDFNAME)] (
  -- Add the parameters for the user-defined function here
  @strInput AS varchar(2)
  )
RETURNS smallint
AS
BEGIN
-- Insert statements for user-defined function here
  DECLARE @intLen integer
  DECLARE @sintN smallint
  DECLARE @sintR smallint
  DECLARE @sintRC smallint

  SET @intLen = Len(@strInput)

  IF ISNUMERIC(@strInput) = 1
    IF (@intLen = 1) Or (@intLen = 2)
      SET @sintRC = CONVERT(smallint, @strInput)
    ELSE
      SET @sintRC = -1
  ELSE
    BEGIN
      IF @intLen = 0
          SET @sintRC = -1
      ELSE IF @intLen = 1
          SET @sintRC = 99 + (ASCII(@strInput) - 64)
      ELSE IF @intLen = 2
        BEGIN
          SET @sintN = 26 * (ASCII(Substring(@strInput, 1, 1)) - 64)
          SET @sintR = ASCII(Substring(@strInput, 2, 1)) - 64
          SET @sintRC = 99 + (CONVERT(smallint, dbo.fTRIM(STR(@sintN + @sintR))))
        END
      ELSE
        SET @sintRC = -1
    END
[COLOR=Red]RETURN @sintRC[/COLOR]
Complaint:
Code:
Msg 102, Level 15, State 1, Procedure frevconv_ConvA2N, Line 36
Incorrect syntax near '@sintRC'.
I believe Line 36 is referring to the LOC in red. Any suggestions?
 
Change return to SELECT or use an OUTPUT parameter
 
Are you actually using this function in-line? if not then I would cahnge it to a stored proc
 
I was thinking to use it as an inline... similar to my fTRIM UDF I use in this UDF. I sense "a fork in the road" in your words. Could you elaborate as to ramifications for each fork, please?

I will consider your input during lunch and a walk starting NOW! :rolleyes: Thank you.
 
I will consider your input during lunch and a walk...

Upon my return I checked my source code for fTRIM. I found an END after the RETURN.

I added that to the frevconv_ConvA2N UDF and now it is accepted by SQL Server. Woo hoo!!! :D

Now onto testing, testing, testing... and then frevconv_ConvN2A.
 

Users who are viewing this thread

Back
Top Bottom