SQL Retrieving values from a statement with multiple selects - sql

I have this SQL:
SELECT count (1) FROM users AS total_drafts WHERE version_replace = #sid
SELECT count (1) AS unpublished_drafts FROM users WHERE version_replace = #sid AND moderated = 0
SELECT * FROM users WHERE id = #sid ORDER By ID DESC
Which appears to be correct. However I'm having difficulty extracting the fields from the results. In vb.net I am using this code fragment:
While r.Read()
Dim o_email As String = CStr(r("email"))
Dim o_first_name As String = CStr(r("first_name"))
Dim o_last_name As String = CStr(r("last_name"))
Which is causing this error: System.IndexOutOfRangeException: email
I have checked the sql is being exucuted correctly. The sql I've posted is simply replacing a simpler statement which was feeding into the code fragment perfectly.
Why is this and how do I correct it?
the correct way:
While r.Read()
total_drafts = CInt(r("total_drafts"))
End While
r.NextResult()
While r.Read()
unpublished_drafts = CInt(r("unpublished_drafts"))
End While
error_status.Text = total_drafts & " " & unpublished_drafts
r.NextResult()
While r.Read()
Dim o_email As String = CStr(r("email"))
Dim o_first_name As String = CStr(r("first_name"))
Dim o_last_name As String = CStr(r("last_name"))

EDIT: r.NextResult() instead of r.ReadNext(), r.ReadNext() is for a DataTableReader
Assuming you are calling the whole sql statement in one go, the problem is that r.Read() will use the first datatable that is returned for the first statement(SELECT count (1) FROM users AS total_drafts WHERE version_replace = #sid ), which does not contain the email etc. fields.
You have to call r.NextResult() twice, this will move the datareader to the 3rd dataset that will contain the data from SELECT * FROM users WHERE id = #sid ORDER By ID DESC

You're returning three datasets. If "r" is a DataReader (unclear from your question) then you need to call;
r.NextResult
between your lines of code, like this;
While r.Read()
Dim o_email As String = CStr(r("email"))
r.NextResult()
Dim o_first_name As String = CStr(r("first_name"))
r.NextResult()
Dim o_last_name As String = CStr(r("last_name"))
One other possible explanation (again, unclear) is that you messed up your first column name ("email"), this would also give an out of range exception.

As far as I can understand you're trying to execute multiple statements, right?
You should separate your SQL statements with a semicolon and change the reader when you've finished with the previous one.
UPDATE:
I usually use stored procedures and return parameters for counters.
Something like this:
CREATE PROCEDURE usp_GetUsers (#sid INT, #unpublished INT OUTPUT)
AS
BEGIN
DECLARE #total_drafts INT
DECLARE #unpublished_drafts INT;
SELECT #total_drafts = count (1) FROM users WHERE version_replace = #sid
SELECT #unpublished_drafts = count (1) FROM users WHERE version_replace = #sid AND moderated = 0
SELECT * FROM users WHERE id = #sid ORDER By ID DESC
RETURN(#total_drafts)
END

Related

need help to form a optimised update sql query

I have a select query with where clause and want to write an update for the same condition.
But I am unable to form one.
So I get the output resultSet and traverse the resultSet to update the table [see below]
The select query:
select
a.businesskey, a.featurekey, c.businesskey, c.featurekey
from
device a,
(select concat(‘customPrefix’,ipaddr,’]’) clmna, instance_id
from wirelessdevice) as b,
device c
where
a.businesskey = b.clmna
and c.uniqueid = b.instance_id;
Present method to update:
string strQuery = "select a.businesskey, a.featurekey, c.businesskey, c.featurekey from device a, (select concat(‘customPrefix’,ipaddr,’]’) clmna, instance_id from wirelessdevice) as b, device c where a.businesskey=b.clmna and c.uniqueid=b.instance_id;";
ResultSet resultSet = statement.executeQuery();
while(resultSet.hasnext()){
String srcbkey = resultSet.getString(1);
String srcFtrkey = resultSet.getString(2);
String destBkey = resultSet.getString(3);
String destFtrkey = resultSet.getString(4);
String updateQuery = "update device set featurekey ='" + destFtrkey +"' where businesskey ='" + srcFtrkey +"';";
statement.executeQuery(updateQuery);
}
is it possible to write a single update query instead of recursively going through the resultset ?
Yes. I find your code hard to follow, but it is something like this:
update device d
set d.featurekey = concat('customPrefix', wd.clmna, wd.ipaddr, ']')
from wirelessdevice wd
where d.businesskey= wd.clmna and
d.uniqueid= wd.instance_id;

Iterate Through Each Record

I have an ASP.NET page which repeatedly calls a SQL stored procedure that pulls a random userID from the table Hearings_Users. What I need now is to pull each UserID in successive order. I would go down the list of UserIDs in the table until the VB function stopped calling it. I'm not sure how to do this in SQL. Thanks
Private Function GetRandomUser() As String
Dim UserID As String = ""
cmd = New SqlCommand("p_Hearings_ActiveUsers", con)
cmd.CommandType = CommandType.StoredProcedure
....
While dr.Read()
UserID = dr("UserID")
End While
End Using
....
Return UserID
End Function
--p_Hearings_ActiveUsers
SELECT TOP 1 UserID FROM Hearings_Users
WHERE Active = 1
ORDER BY NEWID()
I'd create a new stored procedure that takes a parameter of the last userID, and returns the next in sequence
CREATE PROCEDURE spNextUser(#LastUserID INT)
The body would be like p_Hearings_ActiveUsers except it has a condition that the ID is greater than the last used
SELECT TOP 1 UserID
FROM Hearings_Users
WHERE Active = 1
AND UserID > #LastUserID
ORDER BY UserID
When you call this from code, you'll need to add a parameter to the command
Dim paramUserID As SqlParameter = New SqlParameter("LastUserID", SqlDbType.Integer)
paramUserID.Value = dr(UserID)
cmd.Parameters.Add(paramUserID)
but it's otherwise just like the call you have already
This approach insures the state is stored in your application, not server side, so if 2 instances are iterating through they don't cross each other up
Efficiency tip: You don't need to redefine the parameter each loop iteration, you can define it above the loop and inside the loop just update it's value.

Set variable value 0 if select query returns NULL

I have the code below to get the sum of a column from a table.
dim sumX as new oledbcommand
sumX.commandtext = "Select sum(Xcolumn) from [Xtable] where id = "1234""
sumX.commandtype = commandtype.text
sumX.connection = con
Dim z as oledbdatareader = sumX.executereader
If z.read then
y = z.getvalue(0)
End if
The code above works. I want to know what if the code above does not get any value (null), how can I set the value of y to zero if the query's result is null
Try changing the query to this one:
Select IIf(sum(Xcolumn) Is Null,0,sum(Xcolumn)) from [Xtable] where id = "1234"
or this one:
Select Nz(sum(Xcolumn), 0) from [Xtable] where id = "1234"
There's an IIF function in access, it's better to set to 0 in the query.
See if this helps
I just simplified your code to get more performance.It is very useful to use with aggregate functions like Count() or Sum() etc. When compare to ExecuteReader() , ExecuteScalar() uses fewer System resources
Dim sumX As New OleDbCommand
sumX.CommandText = "Select sum(Xcolumn) from [Xtable] where id = 1234"
sumX.CommandType = CommandType.Text
sumX.Connection = con
y = IIf(IsDBNull(sumX.ExecuteScalar), 0, sumX.ExecuteScalar)
ExecuteScalar() in SqlCommand Object is used for get a single value from Database after its execution. It executes SQL statements or
Stored Procedure and returned a scalar value on first column of first
row in the Result Set. If the Result Set contains more than one
columns or rows , it takes only the first column of first row, all
other values will ignore. If the Result Set is empty it will return a
Null reference.

select count on list using linq on specific value vb.net

I am having an issue getting the results I am looking for. What I need to do is essentially:
Select count(invoicenbr) from invoicelist where invoicenbr = 'invoice'
but when I try it in linq I am not getting the correct results.
When I try to execute the linq query below, it gives me the count for the entire list, and not for the where invoicenbr = 'invoice'...
Here is my linq query that is returning the count for the entire invoiceList:
Dim test = (From invoices In invoicelist _
Where e.Row.Cells("invoicenbr").Value = invoice).count()
You have a naming issue in your code, i assume that it's responsible for your problem.
The variable in your query is invoices but later you use invoice to compare it with the cell value, so you have a different variable in scope with name invoice.
This should work:
Dim invoicenbr As String = e.Row.Cells("invoicenbr").Value
Dim duplicates = From invoice In invoicelist
Where invoice = invoicenbr
Dim duplicateCount As Int32 = duplicates.Count()

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.