Top Ten and Update - sql

How do I run a query with VB that will return the TOP 10 results based on column appClickCount and update column appFAIList to 1 and anything below the TOP 10 will give the column appFAIList a value of 0?
Using sqlCon = New SqlConnection("Data Source=(LocalDB)\v11.0;AttachDbFilename=|DataDirectory|\ITCSDatabase.mdf;Integrated Security=True")
sqlCon.Open()
Dim sqlText = "SELECT top 10 appClickCount " & _
"FROM appTable" & _
"UPDATE appTable SET appFAIList = 1"
Dim cmd = New SqlCommand(sqlText, sqlCon)
cmd.ExecuteScalar()
End Using

Run this SQL statement instead
with t as (
select *, rn=row_number() over (order by 1/0)
from appTable)
update t set
appFAIList = case when rn<=10 then 1 else 0 end
Your vb.net code is quite buggy though. You're using ExecuteScalar which is intended to return a single-row, single-column result. As written, it will give you the first appClickCount value.
The other issue is that using TOP 10 in SQL Server without a corresponding ORDER BY means that it will quite arbitrarily (aka "randomly") return any 10 records from the table.

UPDATE a
SET a.appFAIList = 1
FROM appTable AS a
INNER JOIN
(SELECT top 10 appClickCount FROM appTable) AS b ON a.SomeField = b.SomeField
SomeField - may be primary key of your table

Related

VBA SQL returning null values

I'm having a problem running a SQL statement on vba excel, the last 3 Columns are for storing numbers separated by commas, but when executed on excel vba it doesn't display these values, while on other Database programs it does
the code is the following
Sub obtainColMachs()
Dim cnn1 As New ADODB.Connection
Dim mrs As New ADODB.Recordset, sqry As String
Set cnn1 = New ADODB.Connection
cnn1.ConnectionString = "driver=SQL Server Native Client 11.0;" & _
"server=server;uid=user;pwd=password;database=DB;"
cnn1.ConnectionTimeout = 3
cnn1.Open
sqry = "select top 1 m.* from recipe r left join RecipeGroup rg on r.RecipeGroupID = rg.RecipeGroupID " & _
"left join Matricula m on tonalidad_ID = ParentGroupID *100 + rg.RecipeGroupID where Substring(ColorNo,3,3) = 'ZG5'"
mrs.Open sqry, cnn1
Range("A26").CopyFromRecordset mrs
mrs.Close
cnn1.Close
End Sub
It should return:
But it only returns:
You only seem to be returning fields from your Matricula table; perhaps the SQL should be:
select top 1 m.*, r.*, rg.* from
Or better yet, a list of the fields that are actually required and with reference to the table from which they originate, e.g.:
select top 1 m.Tonalidad, m.Field2, r.Field3, rg.Field4 etc... from

Excel VBA ADODB sql query using CTE returning no records

This is my first question on Stack Overflow. I have learned a lot from from this site but I have not been able to find the answer for a problem I am having.
I have a SQL query that works in SQL Server 2008 R2 management studio but it does not work when I query it using vba in EXCEL 2013. The query contains a common table expression and it is not returning any records to my recordset.
The SQL query is:
WITH cte AS
( SELECT *, ROW_NUMBER() OVER (PARTITION BY [partNumber]
ORDER BY [date] DESC) AS i FROM [myDB].[dbo].[PartOrders]
WHERE [partDescription] like '%motor%' )
SELECT * FROM cte WHERE i = 1
I have a reference for Microsoft ActiveX Data Objects 6.1 Library
The VBA code I am using is:
Dim conn as ADODB.Connection
Dim sql as String
Dim rst as ADODB.Recordset
Set conn = New ADODB.Connection
conn.ConnectionString = myConnectionString
conn.Open
sql = ";WITH cte AS ( SELECT *, ROW_NUMBER() OVER (PARTITION BY " & _
"[partNumber] ORDER BY [date] DESC) AS i " & _
"FROM [myDB].[dbo].[PartOrders]" & _
"WHERE [partDescription] like '%motor%' ) " & _
"SELECT * FROM cte WHERE i = 1 "
Set rst = New ADODB.Recordset
rst.Open sql, conn, adOpenStatic, adLockReadOnly, adCmdText
debug.print rst.recordcount
conn.Close
Set rst = Nothing
Set conn = Nothing
My code prints a "-1" in the Immediate window
I have added the preceding ";" to my query based on a recommendation of another questions response. It does not make a difference.
I have verified the following query string returns a recordset:
sql = "SELECT *, ROW_NUMBER() OVER (PARTITION BY " & _
"[partNumber] ORDER BY [date] DESC) AS i " & _
"FROM [myDB].[dbo].[partNumbers]" & _
"WHERE [partDescription] like '%motor%'"
I am using a CTE due to needing to collect entire records of a table but only of distinct part descriptions. I do not want to see that a motor was ordered 20 times. I would like to see that the motor was was ordered at least once along with the other fields associated with it. I am searching a table with 730,000 records where there are records for 10,000 motors but only 500 distinct types.
I am open to using a different query if it will net the same results but I am really curious as to why my current query is not yielding any records. I hope it is not due to an ADODB and VBA incompability.
Thank you for all of the help I have received from others questions and I appreciate any help you can provide me.
Parfait provided a solution for me in the comments to my original question. Changing my connection string provider to a driver did not work. It caused a Run-time error '-2147467259 (80004005)'
Instead of a CTE, I utilized a derived table in my query.
The SQL query that returns a recordcount for me in VBA is:
SELECT main.*
FROM (SELECT *, ROW_NUMBER() OVER (PARTITION BY [partNumber]
ORDER BY [date] DESC) AS i FROM [myDB].[dbo].[PartOrders]
WHERE [partDescription] like '%motor%')
AS main WHERE main.i = 1
Thank You!
I had the same issue except I was using Driver as recommended by user7638417. When the recordset returned, the recordcount was -1, but, when I inserted the rows into the spreadsheet, they were all there (24). Used simplified query in example, actual query was more complex which lead me to the CTE method.
Dim sql As String
Dim dbConn As New ADODB.Connection
Dim dbRS As New ADODB.Recordset
Dim rows As Integer
sql = "WITH S1 AS ( SELECT Lot, CollectDt, Mark, Slot, Thick1, Thick2 From Table1 )"
sql = sql + " WHERE Lot = 'lotnumber' "
sql = sql + " SELECT * FROM S1 WHERE Thick2 <> "UNKNOWN" ORDER BY Slot "
dbConn.ConnectionString = "Driver={SQL Server};server=myserver;database=production;uid=guest;pwd=guest"
dbConn.Open
dbConn.CommandTimeout = 600
dbRS.Open sql, dbConn, adOpenStatic, adLockReadOnly
Dim totRow As Integer
totRow = dbRS.RecordCount + 2
' If (dbRS.RecordCount > 0) Then
Sheet1.Cells(2, 1).CopyFromRecordset dbRS
' End If
I had to comment out the check for data in order to get the data from the record set. The value in totRow came back as 1 instead of 26, the expected value which causes other things to fail later in the code.
SQL Server Version: 13.0.4001.0
Excel Version: 2016

Where clause not working on a SQLite Join request

I need all the unique values of ParteB field on ActPartB table matching the same IDNumber with userActTable but only for the condition of some userIDNumber (on userActTable)
The full sql string with the "WHERE"
sql = "SELECT DISTINCT ParteB FROM ActParteB INNER JOIN userAct ON IDNumber = IDNumber WHERE userAct.userIDNumber = ? order by ParteB asc"
Then I add
cmdConnection.Parameters.AddWithValue("#userIDNumber", NumberOfActiveuser)
The program run with no debugging error but it shows all the unique fields of ActPartB no matter the value of userIDNumber. The value os userIDNumber is fine because.
Is just that the WHERE is "ignored"
This
sql = "SELECT DISTINCT ParteB FROM ActParteB INNER JOIN userAct ON IDNumber = IDNumber WHERE userAct.userIDNumber = ? order by ParteB asc"
Produces the same ouput of this
sql = "SELECT DISTINCT ParteB FROM ActParteB INNER JOIN userAct ON IDNumber = IDNumber order by ParteB asc"
What is wrong with the Where clause?
Full code here:
Using conn As New SQLiteConnection(SQLiteConnStr)
Try
conn.Open()
Dim cmdConnection As SQLiteCommand = New SQLiteCommand(sql, conn)
Dim sql = "SELECT DISTINCT ParteB FROM ActParteB INNER JOIN userAct ON IDNumber = IDNumber WHERE userAct.userIDNumber = ? order by ParteB asc"
cmdConnection.Parameters.AddWithValue("#userIDNumber", NumberOfActiveUser)
Dim readerParteB As SQLiteDataReader = cmdConnection.ExecuteReader()
ParteBComboBox.Items.Clear()
Try
While (readerParteB.Read())
ParteBComboBox.Items.Add(readerParteB("ParteB"))
End While
Catch ex As Exception
MsgBox(ex.ToString())
End Try
Catch ex As Exception
MsgBox(ex.ToString())
End Try
End Using
Dim cmdConnection As SQLiteCommand = New SQLiteCommand(sql, conn)
Dim sql = "..."
Your command does not work correctly because it uses a different SQL string (the value of some other sql variable that you declared in some previous part of the code).
Furthermore, as mentioned by Jinx88909, you must add the table names to the join to specify which IDNumber column you mean (when in doubt, the database will of course choose the wrong one):
... JOIN userAct ON userAct.IDNumber = ActParteB.IDNumber ...
^^^^^^^^ ^^^^^^^^^^
Alternatively, when the column names are the same, better use USING, which automatically gets the column from both tables:
... JOIN userAct USING (IDNumber) ...

SQL output as variable in VB.net

I cannot seem to figure out how to get an output of a SQL query as a variable in VB. I need something like what's below so that I can use that variable in another SQL query. SQL doesn't allow the use of variables as column headers when running queries, so I thought I could use VB to insert the actual output of one SQL task as the dynamic variable in a loop of update queries. Let me (hopefully) explain.
I have the following query:
DECLARE #id int = (SELECT max(id) FROM [views]), #ViewType nvarchar(3);
WHILE #id IS NOT NULL
BEGIN
SELECT #ViewType = (SELECT [View] FROM [views] WHERE id = #id);
UPDATE a
SET a.[#ViewType] = '1'
FROM [summary] a
INNER JOIN [TeamImage] b
ON a.[Part_Number] = b.[PartNum]
WHERE b.[View] = #ViewType;
SELECT #id = max(id) FROM [views] WHERE id < #id;
END;
The SET a.[#ViewType] = '1' obviously will not work in SQL. But if I could have the (SELECT [View] FROM [views] WHERE id = #id) equal to a variable, then I could write the SQL query in VB and execute it and the variable would become part of the string and therefore execute correctly.
I'm newer to VB, but here's what I have so far:
Dim cn As SqlConnection = New SqlConnection("Data Source=Server1;" & _
"Initial Catalog=DB1;" & _
"Integrated Security=SSPI")
cn.Open()
Dim cmd As New sqlCommand("SELECT max(id) FROM orientation_view_experiment;", cn)
vID = cmd.ExecuteNonQuery()
Do While vID > 0
Dim cmd2 As New sqlCommand("SELECT [View] FROM [views] WHERE id ='" + vID + "'"
vViewType = cmd2.ExecuteNonQuery()
Dim cmd3 As New sqlCommand("UPDATE a
SET a.'" + vViewType + "' = '1' & _
FROM [summary] a & _
INNER JOIN [TeamImage] b & _
ON a.[Part_Number] = b.[PartNum] & _
WHERE b.[View] = '" + vViewType + "';"
cmd3.ExecuteNonQuery()
vID = vID - 1
Loop
cn.Close()
I hope some of that made sense, but I'm kind of lost at this point. I feel like I know what I need the SQL to do, but can't quite figure out how to make the computer/programs submit to my will and just do what I need it to do.
Thank you for any help/direction you can give.
Your code is wrong because you insist in using ExecuteNonQuery for SELECT statements. ExecuteNonQuery doesn't return the rows selected but just a count of the rows affected by an INSERT/DELETE/UPDATE query (I think that for SELECT it returns always zero)
What you need is ExecuteScalar to get the MAX value and the VIEW value because ExecuteScalar is the best choice when you expect to get just the first field of the first row from your SQL statement
Dim cmd As New sqlCommand("SELECT max(id) FROM orientation_view_experiment;", cn)
vID = Convert.ToInt32(cmd.ExecuteScalar())
Do While vID > 0
Dim cmd2 As New sqlCommand("SELECT [View] FROM [views] WHERE id =" + vID.ToString()
Dim result = cmd2.ExecuteScalar()
if Not string.IsNullOrEmpty(result)) Then
vViewType = result.ToString()
Dim cmd3 As New sqlCommand("UPDATE a SET a.[" + vViewType + "] = '1' " & _
"FROM [summary] a " & _
"INNER JOIN [TeamImage] b " & _
"ON a.[Part_Number] = b.[PartNum] " & _
"WHERE b.[View] = #vType"
cmd3.Parameters.AddWithValue("#vType", vViewType)
cmd3.ExecuteNonQuery()
End If
Loop
The last part of your code is not really clear to me, but you could use a couple of square brackets around the column name in table summary and a parameter for the View field in table TeamImage.
As a last advice, be sure that the column View in table TeamImage is not directly modifiable by your end user because a string concatenation like this could lead to a Sql Injection attacks
Do a little research into what the different methods of a command are. When you call ExecuteNonQuery, this return the number of records effected. I think you want ExecuteScalar as your cmd and cmd2 methods, so you can get a value from the database.
Have you tried replacing '" + vViewType + "' with [" + vViewType + "] ... in other words use square brackets to delimit the column name instead of single quotes which are for delimiting string literals?
Also, I would encourage stopping in the debugger, examining the command that you generated into cmd3 and try executing it directly. It might help you identify other similar problems such as the fact that vViewType is giving you a count of records instead of an actual value from the [View] column.

How do I implement pagination in SQL for MS Access?

I'm accessing a Microsoft Access 2002 database (MDB) using ASP.NET through the OdbcConnection class, which works quite well albeit very slowly.
My question is about how to implement pagination in SQL for queries to this database, as I know I can implement the TOP clause as:
SELECT TOP 15 *
FROM table
but I am unable to find a way to limit this to an offset as can be done with SQL Server using ROWNUMBER. My best attempt was:
SELECT ClientCode,
(SELECT COUNT(c2.ClientCode)
FROM tblClient AS c2
WHERE c2.ClientCode <= c1.ClientCode)
AS rownumber
FROM tblClient AS c1
WHERE rownumber BETWEEN 0 AND 15
which fails with:
Error Source: Microsoft JET Database Engine
Error Message: No value given for one or more required parameters.
I can't work out this error, but I'm assuming it has something to do with the sub-query that determines a rownumber?
Any help would be appreciated with this; my searches on google have yielded unhelpful results :(
If you wish to apply paging in MS Acces use this
SELECT *
FROM (
SELECT Top 5 sub.ClientCode
FROM (
SELECT TOP 15 tblClient.ClientCode
FROM tblClient
ORDER BY tblClient.ClientCode
) sub
ORDER BY sub.ClientCode DESC
) subOrdered
ORDER BY subOrdered.ClientCode
Where 15 is the StartPos + PageSize, and 5 is the PageSize.
EDIT to comment:
The error you are receiving, is because you are trying to reference a column name assign in the same level of the query, namely rownumber. If you were to change your query to:
SELECT *
FROM (
SELECT ClientCode,
(SELECT COUNT(c2.ClientCode)
FROM tblClient AS c2
WHERE c2.ClientCode <= c1.ClientCode) AS rownumber
FROM tblClient AS c1
)
WHERE rownumber BETWEEN 0 AND 15
It should not give you an error, but i dont think that this is the paging result you want.
See astander's answer for the original answer, but here's my final implementation that takes into account some ODBC parser rules (for the first 15 records after skipping 30):
SELECT *
FROM (
SELECT Top 15 -- = PageSize
*
FROM
(
SELECT TOP 45 -- = StartPos + PageSize
*
FROM tblClient
ORDER BY Client
) AS sub1
ORDER BY sub1.Client DESC
) AS clients
ORDER BY Client
The difference here is that I need the pagination to work when sorted by client name, and I need all columns (well, actually just a subset, but I sort that out in the outer-most query).
I use this SQL code to implement the pagination with Access
Select TOP Row_Per_Page * From [
Select TOP (TotRows - ((Page_Number - 1) * Row_Per_Page)
From SampleTable Order By ColumnName DESC
] Order By ColumnName ASC
I've published an article with some screenshots
on my blog
This is the simple method of pagination using OleDbDataAdapter and Datatable classes. I am using a different SQL command for simplicity.
Dim sSQL As String = "select Name, Id from Customer order by Id"
Dim pageNumber As Integer = 1
Dim nTop As Integer = 20
Dim nSkip As Integer = 0
Dim bContinue As Boolean = True
Dim dtData as new Datatable
Do While bContinue
dtData = GetData(sSQL, nTop, nSkip, ConnectionString)
nSkip = pageNumber * nTop
pageNumber = pageNumber + 1
bContinue = dtData.Rows.Count > 0
If bContinue Then
For Each dr As DataRow In dtData.Rows
'do your work here
Next
End If
Loop
Here is the GetData Function.
Private Function GetData(ByVal sql As String, ByVal RecordsToFetch As Integer, ByVal StartFrom As Integer, ByVal BackEndTableConnection As String) As DataTable
Dim dtResult As New DataTable
Try
Using conn As New OleDb.OleDbConnection(BackEndTableConnection)
conn.Open()
Using cmd As New OleDb.OleDbCommand
cmd.Connection = conn
cmd.CommandText = sql
Using da As New OleDb.OleDbDataAdapter(cmd)
If RecordsToFetch > 0 Then
da.Fill(StartFrom, RecordsToFetch, dtResult)
Else
da.Fill(dtResult)
End If
End Using
End Using
End Using
Catch ex As Exception
End Try
Return dtResult
End Function
The above codes will return 10 rows from the table Customer each time the loop operate till the end of file.
One easy way to use limit or get pagination working in access is to use ADODB library which support pagination for many DBs with same syntax. http://phplens.com/lens/adodb/docs-adodb.htm#ex8
Its easy to modify/override pager class to fetch required number of rows in array format then.
SELECT *
FROM BS_FOTOS AS TBL1
WHERE ((((select COUNT(ID) AS DD FROM BS_FOTOS AS TBL2 WHERE TBL2.ID<=TBL1.ID)) BETWEEN 10 AND 15 ));
Its result 10 to 15 records only.