Help with SQL command not working - sql

I have an SQL command I am attempting to execute.
The syntax is as follows
Public Shared Function CanRaiseWorkOrder(connection As IDbConnection, ProductID As Guid) As Boolean
Using sqlCmd As IDbCommand = connection.CreateCommand
With sqlCmd
.CommandTimeout = 30
.CommandType = CommandType.Text
.CommandText = "DECLARE #CanRaiseWorkOrder BIT, #WorkOrderQtyCount INT, #ProductAvailCount INT SET #WorkOrderQtyCount = (SELECT SUM(Qty) FROM WorkOrder WHERE ProductID = #ProductID AND WorkOrder.Status <> 4) --4 = Voided SET #ProductAvailCount = (SELECT Qty FROM Product WHERE ProductID = #ProductID) IF #WorkOrderQtyCount < #ProductAvailCount BEGIN SET #CanRaiseWorkOrder = 1 END ELSE BEGIN SET #CanRaiseWorkOrder = 0 END SELECT #CanRaiseWorkOrder AS CanRaiseWorkOrder"
Dim params As New List(Of IDbDataParameter)({
ProductDAL.CreateTSqlParameter("#ProductID", DbType.Guid, ProductID)
})
.Parameters.AddRange(params)
Return .ExecuteScaler(Of Boolean)()
End With
End Using
End Function
As you will probably notice there is some customization there in regards to how the parameters are created and the command executed but you can assume those aspects of the system work as required (We have a significant amount of code that functions correctly using those methods).
I will probably get some people asking why this is not a stored procedure and the answer is "because my boss said so".
I have run SQL profiler and here is the output that this query actually generates.
exec sp_executesql N'DECLARE #CanRaiseWorkOrder BIT, #WorkOrderQtyCount INT, #ProductAvailCount INT SET #WorkOrderQtyCount = (SELECT SUM(Qty) FROM WorkOrder WHERE ProductID = #ProductID AND WorkOrder.Status <> 4) --4 = Voided SET #ProductAvailCount = (SELECT Qty FROM Product WHERE ProductID = #ProductID) IF #WorkOrderQtyCount < #ProductAvailCount BEGIN SET #CanRaiseWorkOrder = 1 END ELSE BEGIN SET #CanRaiseWorkOrder = 0 END SELECT #CanRaiseWorkOrder',N'#ProductID uniqueidentifier',#ProductID='0908C780-763F-4CE6-B074-CEC01F4451B4'
Running the code in query analyser (when I originally created it) works fine but if I run the above query as outputted from the SQL command all I get is "Command(s) completed successfully."
Any ideas?

Figure I'll make it an answer, but also wanted to make sure I was accurate (considering how long I've been up now :grin:).
From what I can see (and this is probably due to condensing your statement for the sake of making it a one-liner in a code file) you have a comment declaration in the middle of your statement:
--4 = Voided [...]
What's happening is you're executing only your DECLARE and first SET command (which are done without error) but the rest of your statement is being ignored because it follows the comment declaration (--).
Make sure if you do condense your query that you remove commented lines. Once they're on one line, SQL doesn't care and WILL ignore anything past the --.

Related

VB.net Search Query using two parameters

I have created a search query in VB.net using datasource which has two parameters.
SELECT [Product Code], Description, Input_Date, Price, Unit, Quantity, Markup, Total
FROM Inventory_Table
WHERE (? = ?)
I made two parameters because I want to search by specific columns, this is how i used the query:
Inventory_TableTableAdapter.SearchQuery(DBDataSet1.Inventory_Table, InvSearchCombo.Text, InvSearchTxt.Text)
First parameter would be a dropdown combobox containing all columns from the table, the second parameter would be an input textbox.
But whenever i try searching nothing would appear.
What seems to be the problem? I really want to implement this kind of search feature. Thanks in advance.
In this you can use a dynamic code
Dim columnQuery As String = "Description"
Using command As New SqlCommand( "select Description,Input_Date from dep where " & columnQuery & " = #par1", connection)
command.Parameters.AddWithValue("#par1", "descripcion")
End using
EDIT
A better form could be this:
First, create store procedure:
CREATE PROCEDURE SP_LIST_TABLA_BY_DYNAMIC_COLUMN
#PAR_COLUMN VARCHAR(20),
#PAR_VALUE VARCHAR(20)
AS
DECLARE #STRSQL NVARCHAR(MAX)
SET #STRSQL = 'SELECT PRODUCT_CODE,DESCRIP,INPUT_DATE FROM INVENTORY_TABLE WHERE ' + #PAR_COLUMN + ' = ' + #PAR_VALUE
EXEC sp_executesql #STRSQL
Then invoke it:
Using command As New SqlCommand( "SP_LIST_TABLA_BY_DYNAMIC_COLUMN", connection)
command.CommandType = CommandType.StoredProcedure
command.Parameters.AddWithValue("#PAR_COLUMN", "product_code")
command.Parameters.AddWithValue("#PAR_VALUE", "1")
Using reader As SqlDataReader = command.ExecuteReader()
While reader.Read()
End While
End using
End using
But like user #Basic says: if the column name is coming from user input (even if via a database) then you're going to be vulnerable to SQL injection attacks
One suggestion could be evaluate that par_column name exists and par_value don't have some special characters.
Imagine you have a combo filter and save the ID as #par1 and text field save as #par2
Combo have this values:
none (id: -999)
field1 (id: 0)
field2 (id: 1)
Not sure about how you set your parameter so I will use some pseudo code.
You can make a trick to dynamic set what filter to use.
SELECT *
FROM Inventory_Table
WHERE
(Field1 = #par2 and 0 = #par1)
OR (Field2 = #par2 and 1 = #par1)
OR (-999 = #par1)
So if you select Field1 then 0 = #par1 will be true and first filter will be active
if you select Field2 then 1 = #par1 will be true and second filter will be active
if none is select all rows are return.

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

How to check if park_id already exist in table calendar by stored procedure?

stored procedure
ALTER PROCEDURE [dbo].[KalenderVullen]
#park_id numeric,
#startdate Datetime
AS
BEGIN
;WITH Dates AS (
SELECT DATEADD(day,DATEDIFF(day,0,#startdate),0) as DateOf,
CASE WHEN datename(weekday,#startdate) IN ('Saturday','Sunday') THEN 'Weekend'
ELSE 'Week'
END DayType,
datename(weekday,#startdate) DayOfWeekName
UNION ALL
SELECT DateOf+1,
CASE WHEN datename(weekday,DateOf+1) IN ('Saturday','Sunday') THEN 'Weekend'
ELSE 'Week'
END DayType,
datename(weekday,DateOf+1) DayOfWeekName
FROM Dates
where DateOf < dateadd(yy, 10, #startdate)
)
INSERT INTO kalender (park_id,datum,week_weekend,naam_dag)
SELECT #park_id,DateOf,DayType,DayOfWeekName
FROM Dates
OPTION (MAXRECURSION 4000)
SELECT ##IDENTITY AS [##IDENTITY]
print ##IDENTITY
set #park_id = ##IDENTITY
END
code in VB.NET (DAL)
Public Sub kalender_insert(ByVal i_startdatum As Date, ByVal i_park_id As Int16)
Dim nwindConn As SqlConnection = New SqlConnection(conStr)
Dim adocmd As SqlCommand
Dim adopara As New SqlParameter
Dim mytrans As SqlTransaction
nwindConn.Open()
'begin van commitstructuur
'‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐
mytrans = nwindConn.BeginTransaction(IsolationLevel.ReadCommitted)
adocmd = New SqlCommand("KalenderVullen", nwindConn)
adocmd.CommandType = CommandType.StoredProcedure
adocmd.Parameters.Add(New SqlParameter("#startdate", SqlDbType.DateTime, 50)).Value = i_startdatum
adocmd.Parameters("#startdate").Direction = ParameterDirection.Input
adocmd.Parameters.Add(New SqlParameter("#park_id", SqlDbType.Int, 50)).Value = i_park_id
adocmd.Parameters("#park_id").Direction = ParameterDirection.Input
'‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐
adocmd.Transaction = mytrans
'het committen wordt afhankelijk gemaakt van het slagen
'‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐
Try
adocmd.ExecuteNonQuery()
mytrans.Commit()
Catch ex As Exception
mytrans.Rollback()
End Try
mytrans.Dispose()
mytrans = Nothing
adocmd.Dispose()
adocmd = Nothing
nwindConn.Close()
nwindConn.Dispose()
nwindConn = Nothing
MessageBox.Show("kalender is aangemaakt")
End Sub
code in BLL
????????????
Why assign #park_id with the new value if you've aent it in as a parameter?
And always use SCOPE_IDENTITY
Guessing, try this in the SQL
INSERT INTO kalender (park_id,datum,week_weekend,naam_dag)
SELECT #park_id,DateOf,DayType,DayOfWeekName
FROM Dates
WHERE NOT EXISTS (SELECT * FROM Dates WHERE park_id = #park_id
OPTION (MAXRECURSION 4000);
SELECT #park_id = SCOPE_IDENTITY();
I am not sure If I understand correctly, but maybe you are looking for:
IF EXISTS (SELECT TOP 1 1 FROM kalender WHERE park_id = #park_id) BEGIN
--- here comes you code
END
SELECT TOP 1 1 is efficient form of checking for record existent with given id (assuming that there is a index on park_id column).
#gbn pointed out in comments that TOP 1 1 is not needed. Here are my arguments why they are useful:
Inner query could return a lot of records (depends on query conditions). This could lead to terrible query execution plan -- I see that from time to time on MS SQL 2005.
It matters how much data I read from disk. Using * instead of constant could lead to terrible query plan too. Especially if DB thinks there are a lot of records to return.
Of course if I query by primary key column (or unique column) there should be no more than 1 record. In many other cases -- I would not count on query optimalizer, so I think it's much better to say exactly what I need (in every case). I use TOP 1 1 with EXISTS as idiom.

Sqlcommand Parameters not executing

I am encountering a strange problem when attempting to execute a DELETE query agains a SQL Server table using VB.NET, SQL Command, and Parameters.
I have the following code:
Try
sqlCommand.Transaction = transaction1
sqlCommand.Connection = conn
sqlCommand.CommandText = sqlQuery
sqlCommand.Parameters.Add("#userID", SqlDbType.Int).Value = Convert.ToInt32(userID)
sqlCommand.Parameters.Add("#groupID", SqlDbType.Int).Value = Convert.ToInt32(groupID)
''#Delete the user from the group.
MessageBox.Show("User: " + Convert.ToString(userID) + " Group: " + Convert.ToString(groupID))
MessageBox.Show("Param, UserID: " + sqlCommand.Parameters.Item(0).Value.ToString)
MessageBox.Show("Param, GroupID: " + sqlCommand.Parameters.Item(1).Value.ToString)
return_deleteUser = sqlCommand.ExecuteNonQuery()
Catch ex As Exception
transaction1.Rollback()
Dim hr As Integer = Marshal.GetHRForException(ex)
MsgBox("Removal of user from group has failed: " + ex.Message() & hr)
End Try
Which executes the following SQL Query:
Dim sqlQuery As String = "DELETE FROM MHGROUP.GROUPMEMS WHERE USERNUM =#userID AND GROUPNUM =#groupID"
My problem is that when the code executes, there is no error reported at all. I have ran SQL Profiler and the query doesn't appear in the trace list. The three messageboxes that I have added all return the correct values, and if I was to execute the SQL query against the table with the values the query succeeds. Both the userID and groupID are 3-digit integers.
Can anyone suggest why the code is not working as intended, or any further debugging that I can use to step through the code? Ideally I would love to see the completed SQL query with the parameters completed, but I haven't found out how to do this.
EDIT:
I have the following later in the code to check if the execute's all processed successfully:
If return_insertEvent > 0 And return_updateUser > 0 And return_nextSID > 0 And return_deleteUser > 0 Then
MessageBox.Show("Success")
return_removeADGroup = RemoveUserFromGroup(userID, groupName)
MessageBox.Show("Remove FS User from AD Group: " + return_removeADGroup)
transaction1.Commit()
transaction2.Commit()
transaction3.Commit()
transaction4.Commit()
returnResult = 1
Else
transaction1.Rollback()
transaction2.Rollback()
transaction3.Rollback()
transaction4.Rollback()
returnResult = 0
End If
If you require any further information please don't hesitate in contacting me.
You are missing a Transaction.Commit
Update in respone to additional info added to question:
Why do you have 4 transactions? Since their commit and rollbacks are all executed together, you only need one transaction. I suggest you use a TransactionScope
You can assign the current transaction to ADO.NET Command objects:
ADO.NET and System.Transactions
Transaction Processing in ADO.NET 2.0
I might guess that your calling proc has the values of userid and groupid backwards. If the DELETE doesn't find a matching record, it will complete successfully, but not do anything. I suggest wrapping your delete up in a stored procedure. Then you can add code to test if the parameter values are getting through correctly.
Create Procedure UserDelete
#userid int, #groupID int
As
BEGIN
Select #userid as UID, #groupID as GID INTO TESTTABLE;
DELETE FROM MHGROUP.GROUPMEMS WHERE USERNUM =#userID AND GROUPNUM =#groupID;
END
Run your code then go check the contents of TESTTABLE.
FWIW: I don't like trying to get the whole parameter declaration in one line. Too much going on for me. I like this...
Dim pUID as New Parameter("#userid", SqlDbType.Int)
pUID.Value = userid
cmd.Parameters.Add(pUID)
After some time debugging and sql tracing, I have found out that the stupid application that the DB belongs to treats the group members differently, the groups reside in a administration database, but the users membership to the group resides in another database.
Thank you to everyone above who provided there time and thoughts in assisting with the code. I have changed the code as recomended to use only two transactions and two connections (1 for the admin and sub-database). The code is much nicer now and is that bit easier to read.
Thanks again,
Matt

Return Value from Stored Procedure not set

I have the following very basic stored procedure:
CREATE PROCEDURE [dbo].[GetNumberToProcess]
AS
RETURN 999
I then have some code using Enterprise Library to run and get the return value:
Dim cmd As DbCommand
Dim ResultValue as String
Dim lDBCommand as String = "dbo.GetNumberToProcess"
Dim actionDB As Sql.SqlDatabase = New Sql.SqlDatabase(lConnectionString)
cmd = actionDB.GetSqlStringCommand(lDBCommand)
Dim SQLreturnValue As New SqlParameter("RETURN_VALUE", DbType.Int32)
SQLreturnValue.Direction = ParameterDirection.ReturnValue
cmd.Parameters.Add(SQLreturnValue)
' Execute the command and put the result into the ResultValue for later processing
actionDB.ExecuteNonQuery(cmd).ToString()
ResultValue = cmd.Parameters("RETURN_VALUE").Value.ToString
Problem is that all I ever get back as ResultValue is "0" when I should get "999" (the Stored Proc is very cut down just so that I can get to the bottom of why it's not working).
According to the multiple examples I've seen on the web this should work.
Anyone got any suggestions as to why it doesn't?
Your stored proc is obviously fine, I don't use the EntLib much I think your problem is the line
cmd = actionDB.GetSqlStringCommand(lDBCommand)
Try using this instead
cmd = actionDB.GetStoredProcCommand(lDBCommand)
have your tried
ResultValue = SQLreturnValue.Value.ToString()
I think that's just a syntax thing tho, shouldn't make a diff.
I personally do not have a name for my return param and it works fine:
var returnCode = new SqlParameter();
returnCode.Direction = System.Data.ParameterDirection.ReturnValue;
returnCode.DbType = System.Data.DbType.Int32;
Maybe the name RETURN_VALUE is messing with it?
Change your procedure to be REALLY simple:
ALTER PROCEDURE [dbo].[GetNumberToProcess] AS RETURN 999
Is ResultValue still 0 after this change?
If you are going to get back an output parameter instead of a resultset you need to declare an output parameter in your sproc and assign it a value.
CREATE PROCEDURE [dbo].[GetNumberToProcess]
(
#rtInt as int OUT
)
AS
select #rtInt = 999
Dim SQLreturnValue As New SqlParameter("#rtInt", DbType.Int32)
ResultValue = cmd.Parameters("#rtInt").Value.ToString
My guess is that your code isn't handling the result very well. Short answer: try changing your stored procedure to:
CREATE PROCEDURE [dbo].[GetNumberToProcess]
AS
SET NOCOUNT ON
RETURN 999
GO
The NOCOUNT setting will prvent SQL from sending the '1 record(s) affected' which could influence your return value.