How to write a CTE Recursive Query in VBA script? - sql-server

I have a VBA script written to query HP ALM database using OTA API.
I want to query the database using a Recursive CTE. I don't know how to write that script within this VBA script.
VBA Script:::
Sub Extractor()
Const QCADDRESS = "http://alm/qcbin"
Const DOMAIN = "DOMAIN"
Const PROJECT = "PROJECT"
Const QCUSR = "user.name"
Const QCPWD = "123456"
Dim QCConnection, com, recset
Dim XLS, Wkb, Wks, i
Set QCConnection = CreateObject("TDApiOle80.TDConnection")
QCConnection.InitConnectionEx QCADDRESS
QCConnection.Login QCUSR, QCPWD
QCConnection.Connect DOMAIN, PROJECT
Set com = QCConnection.Command
com.CommandText = "Select * from ALL_LISTS"
Set recset = com.Execute
Set XLS = CreateObject("Excel.Application")
XLS.Visible = False
Set Wkb = XLS.Workbooks.Add
Set Wks = Wkb.Worksheets(1)
i = 1
Wks.Cells(i, 1).Value = "Data"
If recset.RecordCount > 0 Then
i = 2
recset.First
Do While Not (recset.EOR)
Wks.Cells(i, 1).Value = recset.FieldValue(0) 'fieldvalue(0) because the query extract only 1 field.
i = i + 1
recset.Next
Loop
Wkb.SaveAs "C:\myfile.xls"
End If
Wkb.Close
XLS.Quit
QCConnection.Disconnect
Set recset = Nothing
Set com = Nothing
Set QCConnection = Nothing
Set XLS = Nothing
Set Wkb = Nothing
Set Wks = Nothing
End Sub
CTE Query::::
with ReqCTE
as
(
SELECT
RQ_REQ_ID,
RQ_REQ_NAME,
RQ_FATHER_ID,
0 as lvl
FROM
td.REQ
where
RQ_REQ_ID = {?Father_ID}
union all
select
Folders.RQ_REQ_ID,
Folders.RQ_REQ_NAME,
Folders.RQ_FATHER_ID,
Child.lvl +1
from
ReqCTE as Child
join td.REQ as Folders on Folders.RQ_REQ_ID = Child.RQ_FATHER_ID
);
select * from ReqCTE;

Here is your code with your query embedded and your query variable declared as a VBA variable and referenced in the SQL script:
Sub Extractor()
Const QCADDRESS = "http://alm/qcbin"
Const DOMAIN = "DOMAIN"
Const PROJECT = "PROJECT"
Const QCUSR = "user.name"
Const QCPWD = "123456"
Dim par(0) As Variant
Dim QCConnection, com, recset
Dim XLS, Wkb, Wks, i
Set QCConnection = CreateObject("TDApiOle80.TDConnection")
QCConnection.InitConnectionEx QCADDRESS
QCConnection.Login QCUSR, QCPWD
QCConnection.Connect DOMAIN, PROJECT
Set com = QCConnection.Command
par(0) = 4 'set parameter value for Father_ID in SQL
com.CommandText = "with ReqCTE as (" & _
"SELECT RQ_REQ_ID, RQ_REQ_NAME, RQ_FATHER_ID, 0 as lvl FROM td.REQ " & _
"where RQ_REQ_ID = ? " & _
"Union all " & _
"select Folders.RQ_REQ_ID, Folders.RQ_REQ_NAME, Folders.RQ_FATHER_ID, Child.lvl +1 from ReqCTE as Child " & _
"join td.REQ as Folders on Folders.RQ_REQ_ID = Child.RQ_FATHER_ID); " & _
"select * from ReqCTE;"
Set recset = com.Execute(, par)
Set XLS = CreateObject("Excel.Application")
XLS.Visible = False
Set Wkb = XLS.Workbooks.Add
Set Wks = Wkb.Worksheets(1)
i = 1
Wks.Cells(i, 1).Value = "Data"
If recset.RecordCount > 0 Then
i = 2
recset.First
Do While Not (recset.EOR)
Wks.Cells(i, 1).Value = recset.FieldValue(0) 'fieldvalue(0) because the query extract only 1 field.
i = i + 1
recset.Next
Loop
Wkb.SaveAs "C:\myfile.xls"
End If
Wkb.Close
XLS.Quit
QCConnection.Disconnect
Set recset = Nothing
Set com = Nothing
Set QCConnection = Nothing
Set XLS = Nothing
Set Wkb = Nothing
Set Wks = Nothing
End Sub
UPDATED to avoid injection

Related

vba for each element loop error occurs at second loop

I'm new to VBA and I'm trying to scrape data from a website. I've used nested loop. When the innermost loop finishes for the first time, the next loop starts for marakez.
Actual problem is that when 'for each in schl2' loop repeats for second time, IE crashes and loop is unable to proceed. I have mentioned in code.
Here is my code
Sub ResultDownloader()
' here I define elemnts for the loop
Dim sht As Worksheet
Set sht = ThisWorkbook.Sheets("LocData")
Dim LastRow As Long
Dim i As Long
Dim imagePath As Object
LastRow = sht.Cells(sht.Rows.Count, "D").End(xlUp).Row
startrec = sht.Cells(sht.Rows.Count, "E").End(xlUp).Row
startrec = startrec + 1
Dim IE As Object
Dim Doc As HTMLDocument
' Set IE = CreateObject("InternetExplorer.Application")
Set IE = CreateObject("InternetExplorer.Application")
' here I define Object to sendkeys
Dim SHELL_OBJECT
SHELL_OBJECT = "WScript.Shell"
Set objShell = CreateObject(SHELL_OBJECT)
Record2Strt = (sht.Cells(sht.Rows.Count, "E").End(xlUp).Row) + 1
IE.Visible = True
IE.Navigate "some_url"
Do While IE.Busy
Application.Wait DateAdd("s", 1, Now)
Loop
Dim HTMLdoc As HTMLDocument
Dim selectElement, selectElement2, selectElement3 As HTMLSelectElement
Dim evtChange As Object
Set Doc = IE.Document
Dim dist1, tehsl1, mrkz1, schl1 As Object
Dim dist2, tehsl2, mrkz2, schl2 As Variant
Dim distlen, thsllen, mrkzlen, schllen As Byte
Dim dst, tsl, mrkz, schl As Byte
Dim elt3, elt4, elt5, elt6 As Variant
Set evtChange = Doc.createEvent("HTMLEvents")
evtChange.initEvent "change", True, False
Set dist1 = Doc.querySelector("Select[name=districts]")
Set dist2 = dist1.querySelectorAll("option")
distlen = dist1.querySelectorAll("option").Length
dst = 0
For Each elt3 In dist2
distnme = elt3.innerText
If distnme <> "All Districts" Then
dist1.getElementsByTagName("option")(dst).Selected = True
Set selectElement2 = Doc.getElementsByTagName("option")(dst)
selectElement2.dispatchEvent evtChange
Application.Wait DateAdd("s", 0.5, Now)
Set tehsl1 = Doc.querySelector("Select[name=tehsil]")
Set tehsl2 = tehsl1.querySelectorAll("option")
thsllen = tehsl1.querySelectorAll("option").Length
tsl = 0
For Each elt4 In tehsl2
thslnme = elt4.innerText
If thslnme <> "All Tehsils" Then
Set tehsl1 = Doc.querySelector("Select[name=tehsil]")
tehsl1.getElementsByTagName("option")(tsl).Selected = True
Set selectElement3 = tehsl1.getElementsByTagName("option")(tsl)
selectElement3.dispatchEvent evtChange
Application.Wait DateAdd("s", 0.5, Now)
Set mrkz1 = Doc.querySelector("Select[name=markaz]")
Set mrkz2 = mrkz1.querySelectorAll("option")
mrkzlen = mrkz1.querySelectorAll("option").Length
mrkz = 0
For Each elt5 In mrkz2
mrkznm = elt5.innerText
If mrkznm <> "All Marakez" Then
Set mrkz1 = Doc.querySelector("Select[name=markaz]")
mrkz1.getElementsByTagName("option")(mrkz).Selected = True
Set selectElement4 = mrkz1.getElementsByTagName("option")(mrkz)
selectElement4.dispatchEvent evtChange
Application.Wait DateAdd("s", 0.5, Now)
Set schl1 = Doc.querySelector("Select[name=school]")
Set schl2 = schl1.querySelectorAll("option")
schllen = schl1.querySelectorAll("option").Length
schl = 0
' second loop problem
' when for each elt6 in schl2 starts IE crashes
On Error Resume Next
For Each elt6 In schl2
Application.Wait DateAdd("s", 0.5, Now)
schlnm = elt6.innerText
If schlnm <> "All Schools" Then
Set schl1 = Doc.querySelector("Select[name=school]")
schl1.getElementsByTagName("option")(schl).Selected = True
Set selectElement5 = schl1.getElementsByTagName("option")(schl)
selectElement5.dispatchEvent evtChange
sht.Range("A" & LastRow + 1).Value = LastRow
sht.Range("B" & LastRow + 1).Value = distnme
sht.Range("C" & LastRow + 1).Value = thslnme
sht.Range("D" & LastRow + 1).Value = mrkznm
sht.Range("E" & LastRow + 1).Value = schlnm
LastRow = LastRow + 1
End If 'for school
schl = schl + 1
If schllen = schl Then
GoTo new_marakez
On Error Resume Next
End If
Next 'ele6
End If 'for marakez
new_marakez:
mrkz = mrkz + 1
If mrkzlen = mrkz Then
Exit For
GoTo new_tehsil
End If
Next 'ele5
On Error Resume Next
End If 'for tehsils
new_tehsil:
tsl = tsl + 1
If thsllen = tsl Then
GoTo new_dist
End If
Next 'ele4
On Error Resume Next
End If 'for districts
new_dist:
dst = dst + 1
If distlen = dst Then
GoTo stopp
End If
Next 'ele 3
On Error Resume Next
stopp:
End Sub
There is apparently a bug when using querySelectorAll with a generic object for your elements, in your case here 'schl2.', and using a for each...next loop. I solved this by using a standard for...next loop basically limiting the for loop, in your case, schl2.Length - 1. However, this will not work unless you define schl2 as MSHTML.IHTMLDOMChildrenCollection. If you leave this as generic, the schl2.Length will be NULL. The code below shows how I got around the problem.
`'Create html object to hold IE Document
Set html = IE.Document
Debug.Print "********* GET FIELDS ******" & vbCrLf
Dim res1 As MSHTML.IHTMLDOMChildrenCollection
Set res1 = html.querySelectorAll("#HtmlOutputReportResults2_Explorer_Filters_Column option:checked")
For r = 0 To res1.Length - 1
If res1(r).innerText <> "..." Then
Debug.Print "res1.Text: " & res1(r).innerText
End If
Next
Debug.Print vbCrLf & "********* GET OPERATORS ******" & vbCrLf
Dim res2 As MSHTML.IHTMLDOMChildrenCollection
Set res2 = html.querySelectorAll("#HtmlOutputReportResults2_Explorer_Filters_Operator option:checked")
For r = 0 To res2.Length - 1
If res2(r).innerText <> "..." Then
Debug.Print "res2.Text: " & res2(r).innerText
End If
Next`

Upload from Excel vba to SQL Server - generic excel file not working

I want to create a "generic" way of uploading data to a SQL Server database via Excel upload. In my Excel file I have two sheets - one with data (WS_data) and one with the database table design (WS_Table_Design) (e.g. rows of column names).
It is been a while since I coded VBA, but this used to work (I think?) - what am I thinking wrong here?
Errors comes when I want to "post" the values into the database through .Fields([Column]).Value
Function NullCheck(x)
On Error Resume Next
If x = "" Then
NullCheck = Null
Else
NullCheck = x
End If
End Function
Sub Upload_To_DB()
Dim WS_Data, WS_Table_Design As Worksheet
Dim Curent_Row, Curren_Column, Max_Datarows, i, j As Integer
Dim Array_Table_Design()
Set WS_Data = Worksheets("Data")
Set WS_Table_Design = Worksheets("DB_Table_Design")
'List Columns from Table design in array
ReDim Array_Table_Design(0 To 0)
Current_Row = 1
i = 0
Do While WS_Table_Design.Cells(Current_Row, 1).Value <> ""
If WS_Table_Design.Cells(Current_Row, 1).Value > 0 Then
ReDim Preserve Array_Table_Design(0 To i)
Array_Table_Design(i) = WS_Table_Design.Cells(Current_Row, 1).Value
i = i + 1
End If
Current_Row = Current_Row + 1
Loop
'End List Columns
'Find number of rows to upload
Current_Row = 2 'Row where data starts
Max_Datarows = 0
Do While WS_Data.Cells(Current_Row, 1).Value <> ""
Max_Datarows = Max_Datarows + 1
Current_Row = Current_Row + 1
Loop
'End find number of rows to upload
'Upload data according to Table design and Data sheet
Dim con As ADODB.Connection
Dim rs As ADODB.Recordset
Dim sqlstring As String
Set con = New ADODB.Connection
con.Open "Provider=SQLOLEDB.1;Integrated Security=SSPI;Persist Security Info=True;Initial Catalog=Datawarehouse;Data Source=server3"
Set rs = New ADODB.Recordset
rs.Open "select * from FactGL", con, adOpenStatic, adLockOptimistic
Current_Row = 2
Current_Column = 1
i = 0
j = 0
For j = 0 To 5 'Max_Datarows
rs.AddNew
For i = 0 To UBound(Array_Table_Design)
'Error comes here - why can't I add data to the database Table column defined in "Array_Table_Design?
rs.Fields("Array_Table_Design(i)").Value = NullCheck(WS_Data.Cells(Current_Row, i + 1).Value)
i = i + 1
Next i
rs.Update
Current_Row = Current_Row + 1
Next j
End Sub
rs.Fields("Array_Table_Design(i)").Value
should be
rs.Fields(Array_Table_Design(i)).Value

VBS with ADODB Recordset Returning "Could not find prepared statement with handle -1"

I've written a VB Script to go through a folder, extract a record number from the filenames inside, query the corresponding zone for those record numbers, and rename/move the files as appropriate.
First I make a recordset of the files inside (rsFiles), and I build a SQL String from those record numbers as an inlist to query the corresponding zones into a second recordset (rsZones). Then I move through rsZones and write the appropriate zone back to rsFiles.
My code works with a small number of files (I've tested and gotten reliable results up to 200) but I need to process several thousand at a time... If I throw too many files at it, I get error 80040E14 "Could not find prepared statement with handle -1." at the line that does rsZones.MoveFirst.
I think this means rsZones doesn't have any records, but shouldn't that mean rsZones.EOF would resolve to TRUE? Also, I have the ADODB Command timeout set at 0, so why am I not getting records on a larger inlist?
Code extract below. Any help/ideas would be appreciate. Thanks!!
Const sSourceDir = "C:\MyDir\"
Set re = New RegExp
re.Global = False
Set rsFiles = CreateObject("ADOR.Recordset")
rsFiles.Fields.Append "File", adVarChar, MaxCharacters
rsFiles.Fields.Append "Record_Number", adVarChar, 10
rsFiles.Fields.Append "Zone", adVarChar, 2
rsFiles.Open
re.Pattern = "(\d{1,})_\d{1,}_[IPG]\S*.pdf"
Set oFolder = oFSO.GetFolder(sSourceDir)
Set oFiles = oFolder.Files
If oFiles.Count > 0 Then
For each oFile in oFiles
Set reMatches = re.Execute(oFile.Name)
If reMatches.Count > 0 Then
sRecordNumber = reMatches(0).subMatches(0)
sInlist = sInlist & "'" & sRecordNumber & "',"
Else
sRecordNumber = ""
End If
rsFiles.AddNew
rsFiles("File") = oFile.Name
rsFiles("Record_Number") = sRecordNumber
Next
End If
If sInlist = "" Then
MsgBox "No files matching record number pattern"
wScript.Quit
End If
sInlist = Left(sInlist, Len(sInlist)-1)
Const sConn = "Provider=SQLOLEDB;SERVER=my\server;DATABASE=default_database;TRUSTED_CONNECTION=yes"
sSQL = "select tablea.record_number, tablea.zone from tablea where tablea.record_number in (" & sInlist & ")"
Set oConn = CreateObject("ADODB.Connection")
oConn.ConnectionTimeout = 0
oConn.Open sConn
Set cmd = CreateObject("ADODB.Command")
With cmd
.ActiveConnection = oConn
.CommandText = sSQL
.CommandType = 1 'adCmdText
.CommandTimeout = 0
.Prepared = True
End With
set rsZones = CreateObject("ADODB.Recordset")
Set rsZones = cmd.Execute
If Not rsZones.EOF Then
rsZones.MoveFirst 'ERROR IS HERE
Do until rsZones.EOF
rsFiles.Filter = 0
rsFiles.Filter = "record_number = '" & rsZones(0) & "'"
rsFiles.MoveFirst
Do Until rsFiles.EOF
rsFiles("Zone") = rsZones("Zone")
rsFiles.MoveNext
Loop
rsZones.MoveNext
Loop
End If
rsZones.Close

check if SQL field has UNIQUE constraint via ADODB VBA

I am writing vba code to update SQLSERVER tables.
I want to check all values for a new record are ok, before actually trying to update them.
How can I check if a field has UNIQUE constrain?
I have tried listing all properties and attributes, but the propertie ISUNIQUE does not show anything, although it is set as such and it produces error when tring to create a new record with duplicate enty in this field
Properties
BASECATALOGNAME = TEBUS_Templates
BASECOLUMNNAME = Nombre del Armario
BASESCHEMANAME =
BASETABLENAME = TBL_FAKOM_ARMARIOS
CLSID =
COLLATINGSEQUENCE =
COMPUTEMODE =
DATETIMEPRECISION =
DEFAULTVALUE =
DOMAINCATALOG =
DOMAINSCHEMA =
DOMAINNAME =
HASDEFAULT =
ISAUTOINCREMENT = Falso
ISCASESENSITIVE = Falso
ISSEARCHABLE = 4
ISUNIQUE =
OCTETLENGTH = 200
KEYCOLUMN = Falso
OPTIMIZE = Falso
Attributes
adFldUnknownUpdatable
FWIW this is the procedure I put together to get the list above:
'2017-05-22 / B.Agullo /
Public Sub showFieldAtributesAndProperties(ByVal f As Field)
'description of sub
Dim p As Variant
Debug.Print f.Name
Debug.Print "Properties"
For Each p In f.Properties
Debug.Print p.Name & " = " & p.Value
Next
Debug.Print Chr(10) & "Attributes"
If ((adFldCacheDeferred And f.Attributes) = adFldCacheDeferred) Then Debug.Print "adFldCacheDeferred"
If ((adFldFixed And f.Attributes) = adFldFixed) Then Debug.Print "adFldFixed"
If ((adFldIsChapter And f.Attributes) = adFldIsChapter) Then Debug.Print "adFldIsChapter"
If ((adFldIsCollection And f.Attributes) = adFldIsCollection) Then Debug.Print "adFldIsCollection"
If ((adFldIsDefaultStream And f.Attributes) = adFldIsDefaultStream) Then Debug.Print "adFldIsDefaultStream"
If ((adFldIsNullable And f.Attributes) = adFldIsNullable) Then Debug.Print "adFldIsNullable"
If ((adFldIsRowURL And f.Attributes) = adFldIsRowURL) Then Debug.Print "adFldIsRowURL"
If ((adFldLong And f.Attributes) = adFldLong) Then Debug.Print "adFldLong"
If ((adFldMayBeNull And f.Attributes) = adFldMayBeNull) Then Debug.Print "adFldMayBeNull"
If ((adFldMayDefer And f.Attributes) = adFldMayDefer) Then Debug.Print "adFldMayDefer"
If ((adFldNegativeScale And f.Attributes) = adFldNegativeScale) Then Debug.Print "adFldNegativeScale"
If ((adFldRowID And f.Attributes) = adFldRowID) Then Debug.Print "adFldRowID"
If ((adFldRowVersion And f.Attributes) = adFldRowVersion) Then Debug.Print "adFldRowVersion"
If ((adFldUnknownUpdatable And f.Attributes) = adFldUnknownUpdatable) Then Debug.Print "adFldUnknownUpdatable"
If ((adFldUnspecified And f.Attributes) = adFldUnspecified) Then Debug.Print "adFldUnspecified"
If ((adFldUpdatable And f.Attributes) = adFldUpdatable) Then Debug.Print "adFldUpdatable"
release:
End Sub
also, for your reference, this is the SQL command to create the table
CREATE TABLE TBL_FAKOM_ARMARIOS(
"ArmarioID" int IDENTITY(1,1) PRIMARY KEY NOT NULL
, "Nombre del Armario" nvarchar(100) UNIQUE NOT NULL
, "Fecha de Alta" dateTime NOT NULL
, "Fecha de Baja" dateTime
, "Usuario de Alta" nvarchar(50) NOT NULL
, "Usuario de Baja" nvarchar(50)
)
You can use the database schema, something like so
Function IX_UNIQUE(strTableName As String, strFieldName As String) as Boolean
Dim c As ADODB.Connection
Dim r As ADODB.Recordset
Set c = New ADODB.Connection
c.ConnectionString = "Provider=Microsoft.ACE.OLEDB.12.0;Data Source=" & "C:\TESTsb.accdb" & ";" & _
"Persist Security Info=False;"
c.Open
Set r = New ADODB.Recordset
Set r = c.OpenSchema(adSchemaIndexes, Array(Empty, Empty, strFieldName , Empty, strTableName))
If Not r.EOF Then
IX_UNIQUE = r.Fields("UNIQUE").value
Else
IX_UNIQUE = False
End If
End Function

Sql command confusion

Need some SQL advice please
I am using a program called WinCC, in which I am using Vb script language with SQL.
I have a SQL table in which there are 3 columns, TagName, Value and Date
I need to display the Value for each TagName for each day in the chosen month.
The below SQL query give me same value for each day in the month. When I have different values for each day in the month.
Tags(0) = "Tag1"
Tags(1) = "Tag2"
TempTags(0) = "Temp1"
TempTags(1) = "Temp2"
ConnectionString = "Provider=SQL Native Client;Server=WIN81 \WINCC;Database=Teams;Trusted_Connection=yes"
Set Connection = CreateObject("ADODB.Connection")
Set rs = CreateObject ("ADODB.Recordset")
Connection.ConnectionString = ConnectionString
Connection.Open
For i = 0 To 2
SQLStr = "select Value FROM atrperformancestats WHERE TagName = '"+Tags(i)+"'And Month(Date)='"+date+"'"
rs = Connection.Execute(SQLStr)
HMIRuntime.Tags(TempTags(i)).Write rs("Value")
Next
I have tried changing my SQL query to get the value for each day in the user selected month but I am failing miserably...please help
Thank you so much
If the query returns multiple rows, you need a While loop to process the result set. Below is a parameterized example that assumes date is an integer representing the month.
Const adVarChar = 200
Const adInteger = 3
Const adParamInput = 1
Const adCmdText = 1
Tags(0) = "Tag1"
Tags(1) = "Tag2"
TempTags(0) = "Temp1"
TempTags(1) = "Temp2"
ConnectionString = "Provider=SQL Native Client;Server=WIN81 \WINCC;Database=Teams;Trusted_Connection=yes"
Set Connection = CreateObject("ADODB.Connection")
Set Command = CreateObject("ADODB.Command")
Connection.ConnectionString = ConnectionString
Connection.Open
Command.ActiveConnection = Connection
Command.CommandType = adCmdText
Command.CommandText = "SELECT Value FROM atrperformancestats WHERE TagName = ? And Month(Date) = ?;"
Set parmTagName = command.CreateParameter("TagName", adVarchar, adParamInput, 100)
Command.Parameters.Append(parmTagName)
Set parmDate = command.CreateParameter("Date", adInteger, adParamInput)
command.Parameters.Append(parmDate)
For i = 0 To 1
parmTagName.Value = Tags(i)
parmDate.Value = date
Set rs = Command.Execute()
While rs.EOF = False
HMIRuntime.Tags(TempTags(i)).Write rs("Value").Value
rs.MoveNext
WEnd
rs.Close
Next
In WinCC , a SCADA program which is not very code savy I eventually found a way...
j = 0
SQLStr = "Select Value FROM table1 WHERE Name = '" + Names(i) + "' And Month(Date) = '" + MonthSelect + "' order by date asc"
rs.Open SQLStr, Connection
Do Until j = 31
HMIRuntime.Tags(arrayInt(i)).Write rs("Value")
rs.MoveNext
i = i +1
j = j +1
Loop
And it works !!!
Thanks for all the help , your recommendation of a loop got me thinking

Resources