Vbscript SQL select table generator for automated testing - sql

I would like to create a VBScript that read a SQL server database and generate a SQL simple query for each table of one shema of the database and store this SQL into a separate file on disk.
Example :
table A :
ID
field1
field2
field3
table B :
ID
field4
field5
Would generate 2 SQL files :
File 1 : tableA.SQL
SELECT
ID,
field1,
field2,
field3
FROM table A
ORDER BY ID
File 2 : tableB.SQL
SELECT
ID,
field4,
field5
FROM table B
ORDER BY ID
Purpose of this request:
to have an automated testing suite running with all these queries on two copy of the database to find difference on structure and/or data, using NUnit + ORAYLIS BI.Quality http://biquality.codeplex.com/

Given
the decision to use ADOX instead of .OpenSchema
a valid connection to your database (oConn)
a valid path to the folder to store the .sql files in (sDDir)
a global FileSystemObject (goFS)
this
Sub genTS(oConn, sDDir)
Dim oCatalog : Set oCatalog = CreateObject( "ADOX.Catalog" )
Set oCatalog.ActiveConnection = oConn
Dim oTable
For Each oTable In oCatalog.Tables
If "TABLE" = oTable.Type Then
WScript.Echo oTable.Name
ReDim aColumns(oTable.Columns.Count - 1)
Dim i : i = 0
Dim oColumn
For Each oColumn In oTable.Columns
WScript.Echo " ", oColumn.Name
aColumns(i) = oColumn.Name
i = i + 1
Next
Dim sSQL : sSQL = Join(Array( _
"SELECT" _
, "[" & Join(aColumns, "], [") & "]" _
, "FROM [" & oTable.Name & "]" _
, "ORDER BY [" & aColumns(0) & "]" _
), " ")
WScript.Echo " ", sSQL
goFS.CreateTextFile(goFS.BuildPath(sDDir, oTable.Name & ".sql")).WriteLine sSQL
End If
Next
End Sub
should work in principle. Output:
Alpha
Id
StartDate
EndDate
Value
SELECT [Id], [StartDate], [EndDate], [Value] FROM [Alpha] ORDER BY [Id]
...
type ..\data\21751835\Alpha.sql
SELECT [Id], [StartDate], [EndDate], [Value] FROM [Alpha] ORDER BY [Id]
You may have to tinker with the way to specify the order column.
Update:
Maybe the decision (1) was wrong. There may be a way to get a schema name from an ADOX table object, but I can't find it at the moment. So let's use .OpenSchema:
Sub genTS(oConn, sDDir)
Const adSchemaTables = 20
Const adSchemaColumns = 4
Dim rsTables : Set rsTables = oConn.OpenSchema(adSchemaTables, Array(Empty, <YourSchemaName>, Empty, "TABLE"))
Do Until rsTables.EOF
Dim sTName : sTName = rsTables.Fields("TABLE_NAME").Value
WScript.Echo sTName, rsTables.Fields("TABLE_SCHEMA").Value
Dim rsColumns : Set rsColumns = oConn.OpenSchema(adSchemaColumns, Array(Empty, Empty, sTName))
ReDim aColumns(-1)
Do Until rsColumns.EOF
Dim sFName : sFName = rsColumns.Fields("COLUMN_NAME").Value
WScript.Echo " ", sFName
ReDim Preserve aColumns(UBound(aColumns) + 1)
aColumns(UBound(aColumns)) = sFName
rsColumns.MoveNext
Loop
Dim sSQL : sSQL = Join(Array( _
"SELECT" _
, "[" & Join(aColumns, "], [") & "]" _
, "FROM [" & sTName & "]" _
, "ORDER BY [" & aColumns(0) & "]" _
), " ")
WScript.Echo " ", sSQL
goFS.CreateTextFile(goFS.BuildPath(sDDir, sTName & ".sql")).WriteLine sSQL
rsColumns.Close
rsTables.MoveNext
Loop
rsTables.Close
End Sub

Related

ms access vba "Run-time error '3061'. Too few parameters. Expected 7" when querying access query

I google a lot and read all post here related to this issue but found nothing that could give an explanation or help me to resolve this issue.
Following here, a function which work great when the "TableName" parameter is a base table but raise error when it is an ms access view (query). I found nothing yet that could explain this issue as many access query already refer to such views (queries) without issues.
Function DBDistinctCount(FieldName As String, tableName As String) As Long
Dim rs As Recordset, curDb As Database, strSql As String
On Error GoTo ERR_Handling
Set curDb = CurrentDb
'strSql = "SELECT COUNT(PR.[" & FieldName & "]) AS CNT FROM (SELECT [" & FieldName & "] FROM " & TableName & " GROUP BY [" & FieldName & "]) AS PR;"
strSql = "SELECT COUNT(PR." & FieldName & ") AS CNT FROM (SELECT " & FieldName & " FROM " & tableName & " GROUP BY " & FieldName & ") AS PR;"
'strSql = "SELECT COUNT([" & FieldName & "]) AS CNT FROM (SELECT [" & FieldName & "] FROM [" & TableName & "] GROUP BY [" & FieldName & "]);"
'Debug.Print result: SELECT COUNT(PR.ID_Projet) AS CNT FROM (SELECT ID_Projet FROM R_CompilationProjet GROUP BY ID_Projet) AS PR
' Dim qdf As DAO.QueryDef
' Set qdf = curDb.CreateQueryDef(vbNullString, strSql)
Set rs = curDb.OpenRecordset(strSql)
' Set rs = qdf.OpenRecordset(dbOpenSnapshot)
DBDistinctCount = Nz(rs.Fields("CNT"), 0)
ERR_Handling:
If Err.Number <> 0 Then
Dim mess As String
mess = "Erreur vba " & Err.Number & " : " & Err.Description
On Error Resume Next
Call DBHelper.AddLog(mess, "DBDistinctCount")
End If
If Not rs Is Nothing Then rs.Close
Set rs = Nothing
If Not curDb Is Nothing Then curDb.Close
Set curDb= Nothing
End Function
As you can see, I messed up the function a bit in order to find out what could be wrong. I even tried to use a querydef with the same result. I should mention that I've tried to put the resulting sql string itself inside an access query to see exactly the expected result when I ran the query. Any advice would be greatly appreciated.

MS-Access Dynamically Convert Variable Row Values into Variable Column Values Using VBA

Original code link: MS Access - Convert rows values into columns values
I have a follow up to a question where the answer didn't completely resolve, but got super close. It was asked at the original code link above. It's the single page on the net that actually addresses the issue of transposing multiple values in a one-to-many relationship set of columns to a single row for each related value in a dynamic manner specifically using VBA. Variations of this question have been asked about a dozen times on this site and literally none of the answers goes as far as Vlado did (the user that answered), which is what's necessary to resolve this problem.
I took what Vlado posted in that link, adjusted it for my needs, did some basic cleanup, worked through all the trouble-shooting and syntax problems (even removed a variable declared that wasn't used: f As Variant), and found that it works almost all the way. It generates the table with values for the first two columns correctly, iterates the correct number of variable count columns with headers correctly, but fails to populate the values within the cells for each of the related "many-values". So close!
In order to get it to that point, I have to comment-out db.Execute updateSql portion of the Transpose Function; 3rd to last row from the end. If I don't comment that out, it still generates the table, but it throws a Run-Time Error 3144 (Syntax error in UPDATE statement) and only creates the first row and all the correct columns with correct headers (but still no valid values inside the cells). Below is Vlado's code from the link above, but adjusted for my field name needs, and to set variables at the beginning of each of the two Functions defined. The second Function definitely works correctly.
Public Function Transpose()
Dim DestinationCount As Integer, i As Integer
Dim sql As String, insSql As String, fieldsSql As String, updateSql As String, updateSql2 As String
Dim db As DAO.Database, rs As DAO.Recordset, grp As DAO.Recordset
Dim tempTable As String, myTable As String
Dim Var1 As String, Var2 As String, Var3 As String, Var4 As String
tempTable = "Transposed" 'Value for Table to be created with results
myTable = "ConvergeCombined" 'Value for Table or Query Source with Rows and Columns to Transpose
Var1 = "Source" 'Value for Main Rows
Var2 = "Thru" 'Value for Additional Rows
Var3 = "Destination" 'Value for Columns (Convert from Rows to Columns)
Var4 = "Dest" 'Value for Column Name Prefixes
DestinationCount = GetMaxDestination
Set db = CurrentDb()
If Not IsNull(DLookup("Name", "MSysObjects", "Name='" & tempTable & "'")) Then
DoCmd.DeleteObject acTable, tempTable
End If
fieldsSql = ""
sql = "CREATE TABLE " & tempTable & " (" & Var1 & " CHAR," & Var2 & " CHAR "
For i = 1 To DestinationCount
fieldsSql = fieldsSql & ", " & Var4 & "" & i & " INTEGER"
Next i
sql = sql & fieldsSql & ")"
db.Execute (sql)
insSql = "INSERT INTO " & tempTable & " (" & Var1 & ", " & Var2 & ") VALUES ("
Set grp = db.OpenRecordset("SELECT DISTINCT " & Var1 & ", " & Var2 & " FROM " & myTable & " GROUP BY " & Var1 & ", " & Var2 & "")
grp.MoveFirst
Do While Not grp.EOF
sql = "'" & grp(0) & "','" & grp(1) & "')"
db.Execute insSql & sql
Set rs = db.OpenRecordset("SELECT * FROM " & myTable & " WHERE " & Var1 & " = '" & grp(0) & "' AND " & Var2 & " = '" & grp(1) & "'")
updateSql = "UPDATE " & tempTable & " SET "
updateSql2 = ""
i = 0
rs.MoveFirst
Do While Not rs.EOF
i = i + 1
updateSql2 = updateSql2 & "" & Var3 & "" & i & " = " & rs(2) & ", " ' <------- MADE CHANGE FROM (3) to (2)
rs.MoveNext
Loop
updateSql = updateSql & Left(updateSql2, Len(updateSql2) - 1) & " WHERE " & Var1 & " = '" & grp(0) & "' AND " & Var2 & " = '" & grp(1) & "'"
db.Execute updateSql ' <-- This is the point of failure
grp.MoveNext
Loop
End Function
Public Function GetMaxDestination()
Dim rst As DAO.Recordset, strSQL As String
myTable = "ConvergeCombined" 'Value for Table or Query Source with Rows and Columns to Transpose
Var1 = "Source" 'Value for Main Rows
Var2 = "Thru" 'Value for Additional Rows
Var3 = "Destination" 'Value for Columns (Convert from Rows to Columns)
strSQL = "SELECT MAX(CountOfDestination) FROM (SELECT Count(" & Var3 & ") AS CountOfDestination FROM " & myTable & " GROUP BY " & Var1 & ", " & Var2 & ")"
Set rst = CurrentDb.OpenRecordset(strSQL)
GetMaxDestination = rst(0)
rst.Close
Set rst = Nothing
End Function
Sample Table:
Sample Data:
Add a Debug.Print updateSql before that Execute line and will see improper syntax in SQL statement. Need to trim trailing comma from updateSql2 string. Code is appending a comma and space but only trims 1 character. Either eliminate space from the concatenation or trim 2 characters.
Left(updateSql2, Len(updateSql2) - 2)
Concatenation for updateSql2 is using Var3 instead of Var4.
Source field is a number type in ConvergeCombined and this triggers a 'type mismatch' error in SELECT statement to open recordset because of apostrophe delimiters Var1 & " = '" & grp(0) & "' - remove them from two SQL statements.
Also, Source value is saved to a text field in Transposed, make it INTEGER instead of CHAR in the CREATE TABLE action.
So with the help of a friend I figured it out. It turns out I needed two Functions because the one-to-many relationships go both directions in my case. I explain below what needs to happen in comments for this to work. Essentially I went with the second comment under the question I posed (pre-defining field names in static tables because there is a limited number of fields that any person will need - it can't exceed 256 fields anyway, but it isn't always practical to use more than a dozen or so fields - this way allows for both and at the same time to simplify the code significantly).
This solution actually works - but it's dependent on having tables (or queries in my situation) labeled ConvergeSend and ConvergeReceive. Also, it's important to note that the instances where the Destination is single and the Source is plural, the table or query (ConvergeSend/ConvergeReceive) must have the Destination value as a column TO THE LEFT of the iterated Source columns. This is also true (but reverse naming convention) for the other table/query (the Source column must be TO THE LEFT of the iterated Destination columns).
' For this code to work, create a table named "TransposedSend" with 8 columns: Source, Destination1, Destination2,...Destination7; OR however many you need
' Save the table, Edit it, change all field values to Number and remove the 0 as Default Value at the bottom
' Not changing the field values to Number causes the Insert Into function to append trailing spaces for no apparent reason
Public Function TransposeSend()
Dim i As Integer
Dim rs As DAO.Recordset, grp As DAO.Recordset
CurrentDb.Execute "DELETE * FROM TransposedSend", dbFailOnError
CurrentDb.Execute "INSERT INTO TransposedSend (Source) SELECT DISTINCT Source FROM ConvergeSend GROUP BY Source", dbFailOnError
Set grp = CurrentDb.OpenRecordset("SELECT DISTINCT Source FROM ConvergeSend GROUP BY Source")
grp.MoveFirst
Do While Not grp.EOF
Set rs = CurrentDb.OpenRecordset("SELECT Source, Destination, [Destination App Name] FROM ConvergeSend WHERE Source = " & grp(0))
i = 0
rs.MoveFirst
Do While Not rs.EOF
i = i + 1
CurrentDb.Execute "UPDATE TransposedSend SET Destination" & i & " = '" & rs(1) & "', [Destination" & i & " App Name] = '" & rs(2) & "'" & " WHERE Source = " & grp(0)
rs.MoveNext
Loop
grp.MoveNext
Loop
End Function
' For this code to work, create a table named "TransposedReceive" with 8 columns: Destination, Source1, Source2,...Source7; OR however many you need
' Save the table, Edit it, change all field values to Number and remove the 0 as Default Value at the bottom
' Not changing the field values to Number causes the Insert Into function to append trailing spaces for no apparent reason
Public Function TransposeReceive()
Dim i As Integer
Dim rs As DAO.Recordset, grp As DAO.Recordset
CurrentDb.Execute "DELETE * FROM TransposedReceive", dbFailOnError
CurrentDb.Execute "INSERT INTO TransposedReceive (Destination) SELECT DISTINCT Destination FROM ConvergeReceive GROUP BY Destination", dbFailOnError
Set grp = CurrentDb.OpenRecordset("SELECT DISTINCT Destination FROM ConvergeReceive GROUP BY Destination")
grp.MoveFirst
Do While Not grp.EOF
Set rs = CurrentDb.OpenRecordset("SELECT Destination, Source, [Source App Name] FROM ConvergeReceive WHERE Destination = " & grp(0))
i = 0
rs.MoveFirst
Do While Not rs.EOF
i = i + 1
CurrentDb.Execute "UPDATE TransposedReceive SET Source" & i & " = '" & rs(1) & "', [Source" & i & " App Name] = '" & rs(2) & "'" & " WHERE Destination = " & grp(0)
rs.MoveNext
Loop
grp.MoveNext
Loop
End Function

How to delete SQL record in back end database, VBA, Access

I would like to delete all records in the field "pathway" in the table CUSTOMER that is in the backend (offline) database.
So far I have this, but it does not work with DELETE statement
Sub delpath()
Dim dbinputC As String
dbinputC = "[" & Application.CurrentProject.Path & "\CUSTOMER.accdb" & "]"
DoCmd.RunSQL "DELETE pathway FROM " & dbinputC & ".SPECPATH (WHERE pathway <> Null);"
End Sub
Or
Dim dbinputC As String
dbinputC = "'" & Application.CurrentProject.Path & "\CUSTOMER.accdb" & "'"
DoCmd.RunSQL "DELETE pathway FROM SPECPATH (WHERE pathway <> Null) IN " & dbinputC & ";"
Private Sub Test_Clear_Data
Clear_Data "SPECPATH", "Pathway"
End Sub
Private Sub Clear_Data(Table_Name as String, Column_Name As String)
Dim Connection_Path As String
Dim Source_Recset As Object
'Assumes "Clear_Data_Query" already exists
Set Source_Recset = CurrentDB.QueryDefs("Clear_Data_Query")
Source_Recset.SQL = CStr("Update " & Table_Name & " SET [" & Table_Name & "].[" & Column_Name & "] = NULL WHERE [" & Table_Name & "].[" & Column_Name & "] IS NOT Null" & ";")
Source_Recset.Execute
Source_Recset.Close
End Sub
Source_Recset.SQL should get "UPDATE SPECPATH SET [SPECPATH].[Pathway] = NULL Where [SPECPATH].[Pathway] IS NOT NULL;" If table is SPECPATH and Column Name is Pathway
Since you are referencing an offline database i included code to append the table temporarily and remove it after (code is not needed if you leave the table defined in the access file"
Private Sub Clear_Offline_Data(Share_Folder as String, File_Name as String, Table_Name as String, Column_Name As String)
Dim Connection_Path As String
Dim Source_Recset As Object
Dim Destination_Recset As Object
'Create Table To Network Data
Set Destination_Recset = currentDB.CreateTableDef("Offline_Data_Table")
Connection_Path = ";DATABASE=" & ShareFolder & "\" & File_Name
Destination_Recset.Connect = Connection_Path
Destination_Recset.SourceTableName = Table_Name
currentDB.TableDefs.Append Destination_Recset
currentDB.TableDefs.Refresh
'Create Temp_Data From Network Table
Set Source_Recset = CurrentDB.CreateQueryDef("Clear_Data_Query")
Source_Recset.SQL = CStr("Update Offline_Data_Table SET [Offline_Data_Table]." & Column_Name & " = NULL WHERE [Offline_Data_Table]." & Column_Name & " IS NOT Null" & ";")
Source_Recset.Execute
Source_Recset.Close
'Remove Table to network data
currentDB.TableDefs.Delete "Offline_Data_Table"
currentDB.TableDefs.Refresh
'Remove Query
currentDB.QueryDefs.Delete "Clear_Data_Query"
currentDB.QueryDefs.Refresh
End Sub
A delete query deletes rows, not fields.
You must use an update query that updates field pathway.
I guess that it worked after all:
Sub delpath()
Dim dbinputC As String
dbinputC = "[" & Application.CurrentProject.Path & "\CUSTOMER.accdb" & "]"
DoCmd.RunSQL "DELETE pathway FROM " & dbinputC & ".SPECPATH WHERE pathway Is Not Null;"
End Sub

Access Append Query In VBA From Multiple Sources Into One Table, Access 2010

I hardly ever post for help and try to figure it out on my own, but now I’m stuck. I’m just trying to append data from multiple tables to one table. The source tables are data sets for each American State and the append query is the same for each State, except for a nested select script to pull from each State table. So I want to create a VBA script that references a smaller script for each state, rather than an entire append script for each state. I’m not sure if I should do a SELECT CASE, or FOR TO NEXT or FOR EACH NEXT or DO LOOP or something else.
Here’s what I have so far:
tblLicenses is a table that has the field LicenseState from which I could pull a list of the states.
Function StateScripts()
Dim rst As DAO.Recordset
Dim qryState As String
Dim StateCode As String
Set rst = CurrentDb.OpenRecordset("SELECT LicenseState FROM tblLicenses GROUP BY LicenseState;")
' and I've tried these, but they don't work
' qryState = DLookup("LicenseState", "tblLicenses")
' qryState = "SELECT LicenseState INTO Temp FROM tblLicenses GROUP BY LicenseState;"
' DoCmd.RunSQL qryState
Select Case qryState
Case "CT"
StateCode = "CT"
StateScripts = " SELECT [LICENSE NO] AS StateLicense, [EXPIRATION DATE] AS dateexpired FROM CT "
Case "AK"
StateCode = "AK"
StateScripts = " SELECT [LICENSE] AS StateLicense, [EXPIRATION] AS dateexpired FROM AK "
Case "KS"
StateCode = "KS"
StateScripts = " SELECT [LicenseNum] AS StateLicense, [ExpDate] AS dateexpired FROM KS "
End Select
CurrentDb.Execute " INSERT INTO TEST ( StLicense, OldExpDate, NewExpDate ) " _
& " SELECT State.StateLicense as StLicense, DateExpire AS OldExpDate, State.dateexpired AS NewExpDate " _
& " FROM ( " & StateScripts & " ) AS State " _
& " RIGHT JOIN tblLicenses ON (State.StateLicense = tblLicenses.LicenseNum) " _
& " GROUP BY State.StateLicense, DateExpire, State.dateexpired " _
& " HAVING (((LicenseNum) Like '*" & StateCode & "*') ; "
End Function
It sounds like you are dealing with input sources that use different column names for the same information, and you are working to merge it all into a single table. I will make the assumption that you are dealing with 50 text files that are updated every so often.
Here is one way you could approach this project...
Use VBA to build a collection of file names (using Dir() in a specific folder). Then loop through the collection of file names, doing the following:
Add the file as a linked table using VBA, preserving the column names.
Loop through the columns in the TableDef object and set variables to the actual names of the columns. (See example code below)
Build a simple SQL statement to insert from the linked table into a single tables that lists all current license expiration dates.
Here is some example code on how you might approach this:
Public Sub Example()
Dim dbs As Database
Dim tdf As TableDef
Dim fld As Field
Dim strLic As String
Dim strExp As String
Dim strSQL As String
Set dbs = CurrentDb
Set tdf = dbs.TableDefs("tblLinked")
' Look up field names
For Each fld In tdf.Fields
Select Case fld.Name
Case "LICENSE", "LICENSE NO", "License Num"
strLic = fld.Name
Case "EXPIRATION", "EXPIRATION DATE", "EXP"
strExp = fld.Name
End Select
Next fld
If strLic = "" Or strExp = "" Then
MsgBox "Could not find field"
Stop
Else
' Build SQL to import data
strSQL = "insert into tblCurrent ([State], [License],[Expiration]) " & _
"select [State], [" & strLic & "], [" & strExp & "] from tblLinked"
dbs.Execute strSQL, dbFailOnError
End If
End Sub
Now with your new table that has all the new data combined, you can build your more complex grouping query to produce your final output. I like this approach because I prefer to manage the more complex queries in the visual builder rather than in VBA code.
Thanks for your input. I came up with a variation of your idea:
I created table ("tblStateScripts"), from which the rs!(fields) contained the various column names
Dim rs As DAO.Recordset
Dim DB As Database
Set DB = CurrentDb
Set rs = DB.OpenRecordset("tblStateScripts")
If Not rs.EOF Then
Do
CurrentDb.Execute " INSERT INTO TEST ( StLicense, OldExpDate, NewExpDate ) " _
& " SELECT State.StateLicense as StLicense, DateExpire AS OldExpDate, State.dateexpired AS NewExpDate " _
& " FROM ( SELECT " & rs!FldLicenseState & " AS StateLicense, " & rs!FldExpDate & " AS DateExp " & " FROM " & rs!TblState " _
& " RIGHT JOIN tblLicenses ON (State.StateLicense = tblLicenses.VetLicense) " _
& " GROUP BY State.StateLicense, DateExpire, State.dateexpired " _
& " HAVING (((LicenseNum) Like '*" & rs!StateCode & "*') ; "
rs.MoveNext
Loop Until rs.EOF
End If
rs.Close
Set rs = Nothing

dynamic select statement where the parameter count determines the or condition

I have a listbox with multiselect that provides multiple values.I need a select statement from the below
select Amount from tblEmployeeTransactions _
where PayrollCode_Code = '" & code & "' "
my listboxdata is obtained as below
For Each drv As CListItem In lstnoncash.SelectedItems
code =drv.ItemData
Next
my desired query should be
select sum(Amount) from tblEmployeeTransactions _
where PayrollCode_Code = '1' or PayrollCode_Code ='2' or PayrollCode_Code ='3'"
If it has 3 rows of data
You may want something like this :
Dim a As String() = {"1", "2", "3"}
Dim query = "select sum(Amount) from tblEmployeeTransactions " _
& "where PayrollCode_Code IN "
'here inStatement will contain : ('1', '2', '3')'
Dim inStatement = "('" & String.Join("', '", a) & "')"
Console.WriteLine(query & inStatement)
Is you PayrollCode_Code numeric? if yes then you don't need to add single quotation for that. And I prefer to use IN statement instead of multiple OR operator.
Dim sCode As String
For Each drv As CListItem In lstnoncash.SelectedItems
sCode &= drv.ItemData & ","
Next
IF sCode.Length > 0 THEN
sCode = sCode.Substring(0,sCode.Length-1);
Dim _SQL As String = "Select SUM(Amount) FROM tblEmployeeTransactions " &
"WHERE PayrollCode_Code IN (" & sCode & ")"
''Execute ur _SQL
END IF