Prepared Statement in mysql through excel VBA - mysql

I have worked on a prepared statement in Mysql, which dynamically creates week columns based on the current date. It works seamlessly when I run the query in Toad for Mysql, I'm having enormous problems running the query when I am trying to convert the query to a string in VBA......I get an error stating I have a problem with the syntax near etc with the sql. The error is created in every part of the sql despite the fact the string looks fine. I subbed in for " with chr(34), or ' with chr(39) and still I get an error with the sql syntax. I have also tried to avoid these string problems by importing the code below which imports the .sql file to no avail.
Sub TopAway()
Dim Cnn As Object
Dim Rst As Object
Dim ConnectionString, SqlTextFile, SqlStatement As String
Set Cnn = CreateObject("ADODB.Connection")
Set Rst = CreateObject("ADODB.Recordset")
Application.ScreenUpdating = False
ThisWorkbook.Sheets("Summary").Range("A2:T3000").ClearContents
ConnectionString = "Driver={MySQL ODBC 5.3 Unicode Driver};Server = 10.1.1.201; Database = cms; Uid = root; Pwd = something;"
Cnn.Open ConnectionString
Cnn.CommandTimeout = 900
SqlTextFile = "C:\Users\adam\Desktop\WORK FOLDER\Tony Project\Analysis Projects\Away_No_Stock\Dynamic_Away.sql"
Debug.Print SqlTextFile
Dim hFile As Long
hFile = FreeFile
Open SqlTextFile For Input As #hFile
SqlStatement = Input$(LOF(hFile), hFile)
Close #hFile
Debug.Print SqlStatement
Rst.Open SqlStatement, Cnn '<-- This is where the code fails
Sheets("Summary").Range("B3").CopyFromRecordset Rst
End Sub
The SQL code is below.....
SELECT
GROUP_CONCAT(DISTINCT
CONCAT("SUM(IF((part_id IN (265,302,647) OR notes REGEXP ('away|unfit|unavailable|bus away|night')) && WEEKOFYEAR(job_engineerdate) = '", WEEKOFYEAR(job_engineerdate) , "' , 1 ,0)) AS '", WEEKOFYEAR(job_engineerdate), "'")
)
INTO #answers
FROM (
SELECT DISTINCT (job_engineerdate)
FROM job j
ORDER BY (job_engineerdate) ASC
) A
WHERE job_engineerdate >= CURDATE() - INTERVAL 112 DAY AND job_engineerdate <= CURDATE();
SET #query = CONCAT("SELECT customer_name, garage_name, ", #answers, "
FROM (
SELECT c.customer_name, g.garage_name, j.id, jp.part_id, jn.notes, j.job_engineerdate
FROM job j
INNER JOIN vehicle v ON j.job_vehicleid = v.id
INNER JOIN garage g ON v.garage_id = g.id
INNER JOIN customer c ON g.customer_id = c.id
INNER JOIN fault_type ft ON j.job_fault = ft.id
INNER JOIN job_parts jp ON j.id = jp.job_id
INNER JOIN part p ON p.id = jp.part_id
INNER JOIN job_notes jn ON j.id = jn.job_id
INNER JOIN users u ON j.job_engineer = u.id
WHERE
c.customer_group IN (13) AND
j.deleted = 0
AND j.job_engineerdate >= CURDATE() - INTERVAL 112 DAY
GROUP BY
v.vehicle_fleet_number, v.id, j.id, jp.part_id, jn.id
ORDER BY customer_name, garage_name
) AS T
GROUP BY customer_name, garage_name
ORDER BY 18 DESC
LIMIT 15")
;
PREPARE stmt FROM #query;
EXECUTE stmt;
DEALLOCATE PREPARE stmt;
I have no idea how to get this working through excel, if anybody has any ideas, it would be very much appreciated!! I have spent a long time looking on the web for answers but have not been able to find any solutions to this problem. Thanks in advance!

I tackle also with this kind of problem how to translate an sql prepared statement in Excel (VBA), finally I went through with writing a routine in VBA to build the same sql statement as it would be build by the prepared statement.
At the end a prepared statement is a "procedure" you should have no problem translating it in a VBA code.
Hope it helps you (even if I didn't manage to write the VBA code relevant to your case)...

Related

MySqlException was unhandled when using count on SQL query

When my SQL query has a count function in the query it does not want to display the data in the DBGRID and I'm getting a "MySqlException was unhandled" error. As soon as I remove the count function it runs smoothly and displays the data on the DB grid.
Code below:
Dim connection As New MySqlConnection("Server info")
Dim cmd As New MySqlCommand("SELECT COUNT(a.client_id), a.CLIENT_ID,b.c_name, b.C_surname FROM tblinv_info a JOIN tblclientinfo b ON a.CLIENT_ID = b.CLIENT_ID WHERE extract(year from a.inv_date) in ('2018','2019') AND a.Client_id = b.Client_id GROUP BY a.client_id ORDER BY count(a.client_id) desc LIMIT 10", connection)
cmd.CommandTimeout = 500
Dim adapter As New MySqlDataAdapter(cmd)
Dim table As New DataTable()
adapter.Fill(table)
dbreport.DataSource = table
Any idea on why this could be happening? I'm running the program in Visual Studio coding with VB.NET and using a MySql database. The SQL command runs fine on the localhost database.
Error: Error code is as follows: An unhandled exception of type 'MySql.Data.MySqlClient.MySqlException' occurred in MySql.Data.dll
Additional information: Fatal error encountered attempting to read the resultset.
I hope you are well.
I think, the problem is, in the Select query, all the fields have a name column, but the counter, don't.
SELECT COUNT(a.client_id), a.CLIENT_ID,b.c_name, b.C_surname ...
And, the filler and data source don't know where to place the counter column.
Set As name to counter column.
SELECT COUNT(a.client_id) AS Counter1, a.CLIENT_ID,b.c_name, b.C_surname
adapter.Fill(table)
dbreport.DataSource = table
You have missing your fields in group by
Run this in SQL query editor
SELECT COUNT(a.client_id) as cnt, a.CLIENT_ID, b.c_name, b.C_surname -- NOTE CHANGE
FROM
tblinv_info a JOIN
tblclientinfo b ON a.CLIENT_ID = b.CLIENT_ID
WHERE
extract(year from a.inv_date) in ('2018','2019') AND
a.Client_id = b.Client_id
GROUP BY
a.client_id,
b.c_name, -- NOTE CHANGE
b.C_surname -- NOTE CHANGE
ORDER BY
cnt desc -- NOTE CHANGE
LIMIT 10
It sounds like your query timed out during execution.
You can increase command timeout using CommandTimeout.
cmd.CommandTimeout = 120; // 2 minutes (example)
Detailed code:
Dim connection As New MySqlConnection("My Server info")
Dim cmd As New MySqlCommand("SELECT COUNT(a.client_id), a.CLIENT_ID,b.c_name, b.C_surname FROM tblinv_info a JOIN tblclientinfo b ON a.CLIENT_ID = b.CLIENT_ID WHERE extract(year from a.inv_date) in ('2018','2019') AND a.Client_id = b.Client_id GROUP BY a.client_id ORDER BY count(a.client_id) desc LIMIT 10", connection)
cmd.CommandTimeout = 120
Dim adapter As New MySqlDataAdapter(cmd)
Dim table As New DataTable()
adapter.Fill(table)
dbreport.DataSource = table
Ok I finally got the problem sorted.
The mysql.data.dll file I was using as a reference (ver6.3.5.0) was too old.
I downloaded version 6.4.4.0 and removed the old reference and now it displaying perfectly.

Errors in UPDATE query - VB6 DAO Access

A simple UPDATE query with INNER JOIN is causing me problems, using VB6 and DAO on an Access .MDB database.
I thought it would be simple, but whatever changes I make generate errors.
My query is:
UPDATE work
INNER JOIN emp ON work.ref = emp.ref
SET work.code1 = emp.code1
This generates run-time error 3075 Syntax error in query expression 'work.ref = emp.ref'.
I get a similar error with:
UPDATE work w
INNER JOIN emp ON w.ref = emp.ref
SET w.code1 = emp.code1
and
UPDATE [work] w
INNER JOIN emp ON w.ref = emp.ref
SET w.code1 = emp.code1
and also if I use alias e for table emp.
I cannot use a FROM clause, which is not supported in Access (Thanks though #MarkKram)
I have to use DAO 3.51 (old!) in VB6, which cannot easily be changed.
I have tried square brackets around the first reference to work as in UPDATE [work] (as I need this in a simple SELECT * FROM [work]) as well as various combinations with and without square brackets around table names and column names. But still it fails.
Do you have any suggestions please?
Dim ws As DAO.Workspace
Dim DB As DAO.Database
Dim szSQL As String
Set ws = gWS
Set DB = gWS.OpenDatabase(WorkFile)
szSQL = "UPDATE work INNER JOIN emp ON work.ref = emp.ref SET work.code1 = emp.code1 WHERE work.trancode = 'P'"
DB.Execute szSQL
Set DB = Nothing
Set ws = Nothing
Try this:
UPDATE work, emp
SET work.code1 = emp.code1
WHERE work.ref = emp.ref AND work.transcode ='P'

Returned Recordset is closed (mysql DB accessed via ODBC in VBA)

Long time viewer first time poster. I'm working on a database application with a front-end in Excel. Hence I am using VBA to access a MySQL database. The driver I'm using is the ODBC driver (Windows) and I have a valid connection to the database as lots of other queries function well.
What I'm trying to do is return the results from a series of SQL statements rather than trying to combine it all into one massive statement (which would probably work but be difficult to maintain/understand). I have added the flag FLAG_MULTI_STATEMENTS = 67108864 to the connection string which suppressed driver syntax errors.
But now when I run the following:
queryDB.Open SQLquery, conn
The recordset (queryDB) remains closed with no apparent error. The sql statement can be found here.
I may be generating errors that aren't being returned to VBA so any help here would be much appreciated.
NOTE: The sql statements work as I can paste that statement into phpMyAdmin and it returns the correct (non-empty) results. I don't know if the statements specifically are the problem but perhaps the use of CREATE TEMPORARY TABLE ... or the use of multiple statements in general.
Also I guess that the driver may be trying to return a result for each sql statement and VBA is only getting the first or something...
EDIT: the sql statement for future reference.
CREATE TEMPORARY TABLE tmpOrders AS
SELECT
o.customerName,
SUM(o.Sales) AS Sales,
SUM(TotalFobCost + TotalLandedCost + TotalLocalCost + TotalCmtCost) AS TotalCost,
YEAR(o.deliveryDate) AS YEAR,
MONTH(o.deliveryDate) AS MONTH
FROM devere_costing.orders_fixed_extras AS o
WHERE o.orderApproved = TRUE
AND o.orderCanceled = FALSE
AND o.deliveryDate BETWEEN '2014-01-01' AND '2014-03-31'
GROUP BY customerName, YEAR, MONTH
ORDER BY YEAR ASC, MONTH ASC, customerName ASC;
CREATE TEMPORARY TABLE tmpProj AS
SELECT p.customerName,
IF(p.MONTH > 9, p.YEAR, p.YEAR - 1) AS TrueYear,
1 + ((p.MONTH + 2) MOD 12) AS TrueMonth,
SUM(p.actualSalesInvoiced) AS salesInvoiced,
SUM(p.budget) AS budget
FROM devere_costing.sales_projection_data AS p
GROUP BY p.customerName, p.YEAR, p.MONTH
HAVING TrueYear BETWEEN YEAR('2014-01-01') AND YEAR('2014-03-31')
AND TrueMonth BETWEEN MONTH('2014-01-01') AND MONTH('2014-03-31');
CREATE TEMPORARY TABLE tmpLeft AS
SELECT
IF(o.customerName IS NULL, p.customerName, o.customerName) AS customerName,
p.budget AS TotalBudget,
o.Sales AS Sales,
p.salesInvoiced,
0 AS varianceToBudget,
o.TotalCost,
0 AS directMargin,
0 AS directMarginPercent,
IF(o.YEAR IS NULL, p.TrueYear, o.YEAR) AS YEAR,
IF(o.MONTH IS NULL, p.TrueMonth, o.MONTH) AS MONTH
FROM tmpOrders AS o
LEFT JOIN tmpProj AS p
ON (o.customerName = p.customerName
AND o.YEAR = p.TrueYear
AND o.MONTH = p.TrueMonth);
CREATE TEMPORARY TABLE tmpRight AS
SELECT
IF(o.customerName IS NULL, p.customerName, o.customerName) AS customerName,
p.budget AS TotalBudget,
o.Sales AS Sales,
p.salesInvoiced,
0 AS varianceToBudget,
o.TotalCost,
0 AS directMargin,
0 AS directMarginPercent,
IF(o.YEAR IS NULL, p.TrueYear, o.YEAR) AS YEAR,
IF(o.MONTH IS NULL, p.TrueMonth, o.MONTH) AS MONTH
FROM tmpOrders AS o
RIGHT JOIN tmpProj AS p
ON (o.customerName = p.customerName
AND o.YEAR = p.TrueYear
AND o.MONTH = p.TrueMonth);
(SELECT * FROM tmpLeft) UNION DISTINCT (SELECT * FROM tmpRight);
I have answered my own question!
The secret lies here:
So I was right in that there was more than one recordset returned. I just had to iterate through them to find the data that I want. The collection isn't indexed so you have to search through each one. In my case every sql statement does not return a recordset (that's why my recordset remained closed when I tried to open it). The only exception is the last sql statement which returns records. My loop looks like:
Dim rs As ADODB.Recordset
Set rs = queryDB(Sql)
' Loop through returned recordsets to find the data
Do
If Not rs Is Nothing Then
If rs.State = adStateOpen Then
' we have an open recordset. This means that the final select statement
' has returned this data.
Exit Do
Else
' Otherwise iterate through to the next recordset
Set rs = rs.NextRecordset
End If
Else
MsgBox "No recordset returned by sql statement"
GoTo ExitCode
End If
Loop
Answer copied from the question body:
I have answered my own question!
The secret lies here:
So I was right in that there was more than one recordset returned. I just had to iterate through them to find the data that I want. The collection isn't indexed so you have to search through each one. In my case every sql statement does not return a recordset (that's why my recordset remained closed when I tried to open it). The only exception is the last sql statement which returns records. My loop looks like:
Dim rs As ADODB.Recordset
Set rs = queryDB(Sql)
' Loop through returned recordsets to find the data
Do
If Not rs Is Nothing Then
If rs.State = adStateOpen Then
' we have an open recordset. This means that the final select statement
' has returned this data.
Exit Do
Else
' Otherwise iterate through to the next recordset
Set rs = rs.NextRecordset
End If
Else
MsgBox "No recordset returned by sql statement"
GoTo ExitCode
End If
Loop

LINQ Generating Bad Query, Error Unknown Column (VB.NET MySQL)

I'm developing in MVC2 using VB.NET and MySQL and ran into a problem trying to convert a simple SQL query to LINQ.
SQL Query:
SELECT Location_Number, sum(Column1) as totalC1, sum(Column2) as totalC2
FROM MyTable
WHERE year = 2010 and month = 8
GROUP BY Location_Number
ORDER BY Location_Number
LINQ Query:
From r In MyTable _
Where r.Year = 2010 And r.Month = 8 _
Group r By LN = r.Location_Number Into l = Group _
Order By LN _
Select New With { _
.Location_Number = LN, _
.DepositCount = l.Sum(Function(r) r.Column1), _
.OtherCount = l.Sum(Function(r) r.Column2) _
}
The error generated is:
An error occurred while executing the command definition. See the inner exception for details.
The inner exception is:
Unknown column 'GroupBy1.K1' in 'field list'
Here is the SQL generated by LINQ:
SELECT `Project1`.`Location_Number`, `Project1`.`C1`, `Project1`.`C2`
FROM (
SELECT `GroupBy1`.`A1` AS `C1`, `GroupBy1`.`A2` AS `C2`, `GroupBy1`.`K1` AS `Location_Number`
FROM (
SELECT Sum(`Column1`) AS `A1`, Sum(`Column2`) AS `A2`
FROM `MyTable` AS `Extent1`
WHERE (`Extent1`.`Year` = #p__linq__0) AND (`Extent1`.`Month` = #p__linq__1)
GROUP BY `Extent1`.`Location_Number`
) AS `GroupBy1`
) AS `Project1`
ORDER BY `Location_Number` ASC
Looking at that query its easy to spot whats causing the error. Simply, the most inner query only returns 2 columns, while the query right above it is trying to SELECT 3, thus the Unknown Column Error. So why is this happening? What is wrong with my LINQ query?
Thank you
It is a MySQL connector bug: http://bugs.mysql.com/bug.php?id=46742
Is not fixed in last (6.3.5) version.
Good news! That bug was fixed on 6.3.7 (see comments in http://bugs.mysql.com/bug.php?id=46742).
Yes, it's a mySQL connector bug.
Try this:
Using context = New YourContext()
Dim sql = "SELECT Location_Number, sum(Column1) as totalC1, sum(Column2) as totalC2 FROM MyTable WHERE year = #YEAR and month = #MONTH GROUP BY Location_Number ORDER BY Location_Number"
Dim args = New DbParameter()
{
New SqlParameter() With {Key .ParameterName = "YEAR", Key .Value = 2010}, New SqlParameter() With {Key .ParameterName = "MONTH", Key .Value = 8}
}
Return context.ExecuteStoreQuery(Of MyTable)(sql, args).ToList()
End Using

Why would AccessDataSource return different results to query in Access?

I have a query to return random distinct rows from an Access database. Here is the query:
SELECT * FROM
(SELECT DISTINCT m.MemberID, m.Title, m.FullName, m.Address,
m.Phone, m.EmailAddress, m.WebsiteAddress FROM Members AS m INNER JOIN MembersForType AS t ON m.MemberID = t.MemberID WHERE
(Category = 'MemberType1' OR Category = 'MemberType2')) as Members
ORDER BY RND(members.MemberID) DESC
When I run this in Access it returns the rows in different order every time, as per the random sort order. When I run it through my web app however the rows return in the same order every time. Here is how I call it in my code-behind:
private void BindData()
{
using (AccessDataSource ds = new AccessDataSource("~/App_Data/mydb.mdb", GetSQLStatement()))
{
ds.DataSourceMode = SqlDataSourceMode.DataReader;
ds.CacheDuration = 0;
ds.CacheExpirationPolicy = DataSourceCacheExpiry.Absolute;
ds.EnableCaching = false;
listing.DataSource = ds.Select(new DataSourceSelectArguments());
listing.DataBind();
if (listing.Items.Count == 0)
noResults.Visible = true;
else
noResults.Visible = false;
}
}
I added in all that stuff about caching because I thought maybe the query was being cached but the result was the same. I put a breakpoint in the code to make sure the query was the same as above and it was.
Any ideas? This is driving me nuts.
When executing the ACE/Jet RND function against a new connection the same seed value is used each time. When using MS Access you are using the same connection each time, which explains why you get a different value each time.
Consider these VBA examples: the first uses a new connection on each iteration:
Sub TestDiff()
Dim con As Object
Set con = CreateObject("ADODB.Connection")
With con
.ConnectionString = _
"Provider=MSDataShape;Data " & _
"Provider=Microsoft.ACE.OLEDB.12.0;" & _
"Data Source=C:\Tempo\Test_Access2007.accdb"
.CursorLocation = 3
Dim i As Long
For i = 0 To 2
.Open
Debug.Print .Execute("SELECT RND FROM OneRowTable;")(0)
.Close
Next
End With
End Sub
Output:
0.705547511577606
0.705547511577606
0.705547511577606
Note the same value each time.
The second example uses the same connection on each iteration (the .Open and .Close statements are relocated outside the loop):
Sub TestSame()
Dim con As Object
Set con = CreateObject("ADODB.Connection")
With con
.ConnectionString = _
"Provider=MSDataShape;Data " & _
"Provider=Microsoft.ACE.OLEDB.12.0;" & _
"Data Source=C:\Tempo\Test_Access2007.accdb"
.CursorLocation = 3
.Open
Dim i As Long
For i = 0 To 2
Debug.Print .Execute("SELECT RND FROM OneRowTable;")(0)
Next
.Close
End With
End Sub
Output:
0.705547511577606
0.533424019813538
0.579518616199493
Note different values each time.
In VBA code you can use the Randomize keyword to seed the Rnd() function but I don't think this can be done in ACE/Jet. One workaround is to use the least significant decimal portion of the ACE/Jet the NOW() niladic function e.g. something like:
SELECT CDBL(NOW()) - ROUND(CDBL(NOW()), 4) FROM OneRowTable
I would move the RND into the inner SELECT
SELECT * FROM
(SELECT DISTINCT m.MemberID, RND(m.MemberID) as SortOrder, m.Title,
m.FullName, m.Address, m.Phone, m.EmailAddress, m.WebsiteAddress
FROM Members AS m
INNER JOIN MembersForType AS t ON m.MemberID = t.MemberID
WHERE
(Category = 'MemberType1' OR Category = 'MemberType2')) as Members
ORDER BY
Members.SortOrder DESC
You can use time as one argument to the RND field
Dim Now As DateTime = DateTime.Now
Dim millSec As Integer = Now.Millisecond
finalQuery = "SELECT * FROM wordInfo ORDER BY Rnd(-(1000* ROUND(" + millSec.ToString("N") + ", 0)) * [ID])"
So here from date and time value, millisecond value is taken which will be integer and it is used in sql query by rounding it.
wordInfo is table name
ID is the column name in database table
This gives random order every time (since millisecond value is different) be it same connection or new connection.