MajP
You've got your good things, and you've got mine.
- Local time
- Today, 08:45
- Joined
- May 21, 2018
- Messages
- 8,935
If you follow my threads, I try to demonstrate how to write code that is flexible, generic, encapsulated, fault proof, and debuggable. My goal is always to try to write code once that can be re-used often and everywhere. I do this in functions, procedures, and class modules. This may require a lot of work up front, but in the long run it makes building applications so much easier.
Probably the biggest area I see people struggle and often painfully, is writing proper SQL to use in filters or other SQL code. The biggest issue is how to properly delimit literal values and pulling this information from a control.
The following discussion are all the things you normally have to do to get the SQL string correct. The provided code does all of this for you.
Strings: Strings used in SQL must be wrapped in quotes. If that string contains a single quote inside the string, it needs to be replaced by 2 single quotes. Ex
“…WHERE Some Field = ‘Dog’…”
If the original value is O’Brien it needs to get converted to
“…WHERE SomeField = ‘O’’Brien’…”
You also want to make sure to trim the values so you do not get mistakenly
“…WHERE SomeField = ‘ Dog ’…”
The CSQL will do this for you.
Dates: Dates give most people the biggest issue. Dates HAVE TO BE IN US FORMAT MM/DD/YYYY or some other non ambiguous format and surrounded by number signs. Regardless of any regional format you have, it still requires this format.
“…WHERE SomeField = #05/19/2020# …”
If it has a time component
“…WHERE SomeField = #5/19/2020 9:53:04 AM# …”
So writing this out can get long and confusing. Ex:
“…. WHERE SomeField = #” & Format(someDate,”MM/DD/YYYY”) & “#”
Booleans: Booleans can be forgiving depending on how called, but to ensure it works the best is
“…WHERE SomeField = -1 …” true
“…WHERE SomeField = 0…” false
Numbers: Do not get delimited
“…WHERE SomeField = 123.45 …”
Nulls: Null values need to get converted to the word NULL
"... Where SomeField IS NULL"
"INSERT into SomeTable (Field1, Field2) values (123, NULL).. "
So I recommend to save a lot of headaches to put this function in your library because it does this formatting for you. Use it like other conversion functions (Cdate, Clng, Ccur)
CSql(ByVal Value As Variant, Optional Sql_Type As SQL_DataType = sdt_UseSubType)
At the top are some optional enumerated constants to force the data type. This may or may not be necessary.
If you pass in a bound control value it can determine the data type. If you pass in a typed variable it will know. If you pass in a variant but it is subtyped it will know. In these cases no need to specificy the Sql_DataType. However there may be no way to know so you have to specify. For example you have an unbound text box and you type in a date. You need to pass in the Sql_Type. Or if you want to convert from one to another. Assume your date is a string but you want to use it as a date. Examples:
Results
Note that O'Brien changed to O''Brien
Note that if you create a variant but assign it a date, string, or number it knows the subtype.
Note how it converts date strings to date format or vice versa when specified using the SQL_Datatype
So to use this you can replace
“…. WHERE SomeField = #” & Format(someDate,”MM/DD/YYYY”) & “#”
with
“…. WHERE SomeField = " & csql(SomeDate)
or
“…. WHERE SomeField = #” & Format(someDate,”MM/DD/YYYY”) & “# AND SomeOtherField = '” & SomeTextField & "'"
“…. WHERE SomeField = ” & csql(someDate) & “ AND SomeOtherField = ” & csql(SomeTextField)
Probably the biggest area I see people struggle and often painfully, is writing proper SQL to use in filters or other SQL code. The biggest issue is how to properly delimit literal values and pulling this information from a control.
The following discussion are all the things you normally have to do to get the SQL string correct. The provided code does all of this for you.
Strings: Strings used in SQL must be wrapped in quotes. If that string contains a single quote inside the string, it needs to be replaced by 2 single quotes. Ex
“…WHERE Some Field = ‘Dog’…”
If the original value is O’Brien it needs to get converted to
“…WHERE SomeField = ‘O’’Brien’…”
You also want to make sure to trim the values so you do not get mistakenly
“…WHERE SomeField = ‘ Dog ’…”
The CSQL will do this for you.
Dates: Dates give most people the biggest issue. Dates HAVE TO BE IN US FORMAT MM/DD/YYYY or some other non ambiguous format and surrounded by number signs. Regardless of any regional format you have, it still requires this format.
“…WHERE SomeField = #05/19/2020# …”
If it has a time component
“…WHERE SomeField = #5/19/2020 9:53:04 AM# …”
So writing this out can get long and confusing. Ex:
“…. WHERE SomeField = #” & Format(someDate,”MM/DD/YYYY”) & “#”
Booleans: Booleans can be forgiving depending on how called, but to ensure it works the best is
“…WHERE SomeField = -1 …” true
“…WHERE SomeField = 0…” false
Numbers: Do not get delimited
“…WHERE SomeField = 123.45 …”
Nulls: Null values need to get converted to the word NULL
"... Where SomeField IS NULL"
"INSERT into SomeTable (Field1, Field2) values (123, NULL).. "
So I recommend to save a lot of headaches to put this function in your library because it does this formatting for you. Use it like other conversion functions (Cdate, Clng, Ccur)
CSql(ByVal Value As Variant, Optional Sql_Type As SQL_DataType = sdt_UseSubType)
Code:
Public Enum SQL_DataType
sdt_boundfield = -1
sdt_UseSubType = 0
sdt_text = 1
sdt_Numeric = 2
sdt_date = 3
sdt_Boolean = 4
sdt_Null = 5
End Enum
'**************************************************************************************************************
'----------------------------------------Convert To Delimited SQL When Datatype can be Determined -------------
'**************************************************************************************************************
Public Function CSql(ByVal Value As Variant, Optional Sql_Type As SQL_DataType = sdt_UseSubType) As String
'Can be used when the Value is subtyped. For example you pass a declared variable
Const SqlNull As String = "Null"
Dim Sql As String
'If the Sql_type is not passed then use the data type of the value
If Trim(Value & " ") = "" Then
CSql = SqlNull
Else
If Sql_Type = sdt_UseSubType Then
Select Case VarType(Value)
Case vbEmpty, vbNull
Sql_Type = sdt_Null
Case vbInteger, vbLong, vbSingle, vbDouble, vbCurrency, vbDecimal, vbByte
Sql_Type = sdt_Numeric
Case vbDate
Sql_Type = sdt_date
Case vbString
Sql_Type = sdt_text
Case vbBoolean
Sql_Type = sdt_Boolean
Case Else
Sql_Type = sdt_Null
End Select
End If
Select Case Sql_Type
Case sdt_text
Sql = Replace(Trim(Value), "'", "''")
If Sql = "" Then
Sql = SqlNull
Else
Sql = " '" & Sql & "'"
End If
Case sdt_Numeric
If IsNumeric(Value) Then
Sql = CStr(Value)
Else
MsgBox "Invalid data: " & Value & ". You specified a numeric data type", vbInformation
Exit Function
End If
Case sdt_date
If IsDate(Value) Then
If Int(CDate(Value)) = Value Then
Sql = Format$(Value, "\#mm\/dd\/yyyy\#")
Else
Sql = Format$(Value, "\#mm\/dd\/yyyy hh\:nn\:ss\#")
End If
Else
MsgBox "Invalid data: " & Value & ". You specified a date data type", vbInformation
Exit Function
End If
Case sdt_Boolean
If Value = "True" Or Value = "False" Or Value = -1 Or Value = 0 Or Value = "Yes" Or Value = "No" Then
If Value = "True" Or Value = "Yes" Then Value = -1
If Value = "False" Or Value = "No" Then Value = 0
Sql = Str(Value)
Else
MsgBox "Invalid data: " & Value & ". You specified a boolean data type", vbInformation
Exit Function
End If
Case sdt_Null
Sql = SqlNull
End Select
CSql = Trim(Sql)
End If
End Function
At the top are some optional enumerated constants to force the data type. This may or may not be necessary.
If you pass in a bound control value it can determine the data type. If you pass in a typed variable it will know. If you pass in a variant but it is subtyped it will know. In these cases no need to specificy the Sql_DataType. However there may be no way to know so you have to specify. For example you have an unbound text box and you type in a date. You need to pass in the Sql_Type. Or if you want to convert from one to another. Assume your date is a string but you want to use it as a date. Examples:
Code:
Public Sub TestCSql()
Dim blnVal As Boolean
Dim dblVal As Double
Dim lngVal As Long
Dim dtVal As Date
Dim dtTmVal As Date
Dim txtVal As String
Dim unkValdate As Variant
Dim unkValStr As Variant
Dim unkVal As Variant
blnVal = True
dblVal = 2.02
lngVal = 7
dtVal = Date
dtTmVal = Now
txtVal = "Dog"
unkValdate = Now + 7
unkValStr = "Cat"
unkVal = Null
'Using the known data type or sub type
Debug.Print CSql(blnVal)
Debug.Print CSql(dblVal)
Debug.Print CSql(lngVal)
Debug.Print CSql(dtVal)
Debug.Print CSql(dtTmVal)
Debug.Print CSql(txtVal)
Debug.Print CSql(unkValdate)
Debug.Print CSql(unkValStr)
Debug.Print CSql(unkVal)
Debug.Print CSql(" O'Brien ")
'Using defined data types
Debug.Print vbCrLf & " Using Data types"
Debug.Print CSql(Format(Now), sdt_date)
Debug.Print CSql("O'Brien")
Debug.Print CSql(1.23, sdt_Numeric)
Debug.Print CSql("1.23", sdt_Numeric)
Debug.Print CSql(1.23, sdt_text)
Debug.Print CSql("No", sdt_Boolean)
Debug.Print CSql(True, sdt_Boolean)
Debug.Print CSql(Null, sdt_date)
Debug.Print CSql(Now, sdt_text)
Debug.Print CSql("1/1/2020", sdt_date)
End Sub
Results
Code:
-1
2.02
7
#05/19/2020#
#05/19/2020 10:27:46#
'Dog'
#05/26/2020 10:27:46#
'Cat'
Null
'O''Brien'
Using Data types
#05/19/2020 10:27:46#
'O''Brien'
1.23
1.23
'1.23'
0
-1
Null
'5/19/2020 10:27:46 AM'
#01/01/2020#
Note that if you create a variant but assign it a date, string, or number it knows the subtype.
Note how it converts date strings to date format or vice versa when specified using the SQL_Datatype
So to use this you can replace
“…. WHERE SomeField = #” & Format(someDate,”MM/DD/YYYY”) & “#”
with
“…. WHERE SomeField = " & csql(SomeDate)
or
“…. WHERE SomeField = #” & Format(someDate,”MM/DD/YYYY”) & “# AND SomeOtherField = '” & SomeTextField & "'"
“…. WHERE SomeField = ” & csql(someDate) & “ AND SomeOtherField = ” & csql(SomeTextField)
Attachments
Last edited: