Iterate Through Each Record - sql

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.

Related

How to Safe input order by with SQLCommand in VbNet using SQLServer

Select cod,nom from tb_user where cod > #param0 order by #param1
Dim mycod = 3
Dim myorderby = "asc"
Dim _adapter = New SqlDataAdapter
cmd.CommandTimeout = timeout
cmd.Connection = _conn
cmd.CommandText = pSql
cmd.CommandType = CommandType.Text
Dim sqlParameter0 = New SqlParameter("#param0", mycod)
cmd.Parameters.Add(sqlParameter0)
Dim sqlParameter1 = New SqlParameter("#param1", myorderby)
cmd.Parameters.Add(sqlParameter1)
_adapter.SelectCommand = cmd
_adapter.Fill(_ds, "result")
I know I must replace the #param0 by the value of my variable mycod to be safe.
This is possible in the variables like the param0, but the #param1 where I put asc it gives me the following error:
the SELECT item identified by the ORDER BY number 1 contains a variable as part of the expression identitying a column position
PS: By the error it is clear the SqlParameter is not the way to input this kind of order by. Is there a way to input this kind of query safely?
You can do this by selectively ordering on the two columns.
Select cod,nom from tb_user
where cod > #param0
order by
case when #param1=1 then cod else 0 end,
case when #param1=2 then nom else 0 end
There are techniques to dynamically sort by a parameter but this can often lead to a significant slow down. I had a nightmare situation when I tried something similar.
The query worked well most of the time, but the query plan created was completely inappropriate when using a different parameter value, and the results took forever to return..
Since you're binding to a DataSet, you should just sort on the DefaultView after you call Fill().
_ds.Tables(0).DefaultView.Sort = myorderby

SqlDataReader read whole query

I have a SQL script with a loop. In each iteration I change the where clause. So I get several selects displayed. But my .net program reads only the first select. The SQL script works in ssms.
This is my SQL script
while (#aktuellParam <= #countParam)
Begin
SELECT name1,
name2
FROM dbo.tableName
WHERE name like #var
SET #aktuellParam = aktuellParam+1
END
This is my vb.net code
Using reader As SqlClient.SqlDataReader = _server.ConnectionContext.ExecuteReader(script)
Dim lfdnr As Integer = 1
Do While reader.Read()
spaltenListe.Add(New ISpalten With
{
.Name1= reader.GetString(reader.GetOrdinal("name1")),
.Name2= reader.GetString(reader.GetOrdinal("name2"))
})
lfdnr = lfdnr + 1
Loop
reader.Close()
End Using
That's because subsequent selects are actually in a different result set. Your reader needs to do a .NextResult after each read.

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.

Read row by row vb.net

I need to read row by row in a column in a table then I need to store this then call procedure to insert data to a different column using vb.net.
I have already create the DB connection and I know how to call the procedure
but I'm not sure of how to read in the loop and then to assign it to a variable to call it in the store procedure.
Dim drDocs As SqlClient.SqlDataReader
Dim cmdDocs As SqlClient.SqlCommand
Dim Doc As Long
Dim l As Long
Using conn As New SqlConnection(DBpath)
cmdDocs = New SqlClient.SqlCommand("Select (RecordID) from DocID", conn)
drDocs = cmdDocs.ExecuteReader
Do While drDocs.Read
'need it read each row in that field and hold value'
Loop
drDocs.Close()
cmdDocs.Dispose()
If Doc Then
cmdDocs = New SqlClient.SqlCommand("Insert_Doc", conn)
cmdDocs.CommandType = CommandType.StoredProcedure
cmdDocs.Parameters.Add("path", SqlDbType.NVarChar).Value =need to put hold value from reading that cloumn row by row
End If
End If
The code you've provided actually works now. It is as Juergen D says, sql functions like Max(), min() and using Limit will only return 1/certain number of rows based on their conditions.
if I may, just use this SQL command
"select `RecordID` from DocID asc;"
If you want it in descending format, use desc instead
...now reading further, I realize that what you want to do is to store the results, then loop again through it so that you can do an sql command with it, correct? what you can do then is to pass the SQL results to a container (I use datagridviews) then loop through the container.

SQL Retrieving values from a statement with multiple selects

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