Help Constructing an Oracle SQL with Condition - sql

I need some help in creating an Oracle SQL which I will execute in .NET.
I need to update a column in a table but the value to update the same would be dependent on two different values. To give an example:
Dim sqlcmd as String
Dim collCmd as Collection
For x = 1 to intCount
sqlcmd = "Update tableA Set col1 = :val1, col2 = :val2 Where...."
collcmd.add(sqlcmd)
SELECT col1, col2
FROM tableA
Where .....
If col1 = 0 and col2 = 0 then
sqlcmd = "Update tableB
Set col1 = :value
Where...."
Else
sqlcmd = "Update tableB
Set col1 = :value
Where.."
End If
collcmd.add(sqlcmd)
Next
'Perform the update with transaction here for the collcmd collection.
Apparently, I need to place the update in one sql where the condition is met. Kindly advise. I cannot do a one time execute non query here since if one of the update fails, then I would need to perform a transaction rollback. I am placing all the update statement in one collection and performing the update in one transaction. But the value for the tableA may be different on the next iteration.
Kindly take note that I cannot place the same inside a stored proc since there are other sql commands which are executed prior to the statements above.
Is there a way to create an SQL where the update would go something like:
sqlcmd = "UPDATE tableB b
IF select a.col1 = 0 and select a.col2 = 0 from tableA a
SET b.col1 = "this value"
ELSE
SET b.col1 = "other value"
WHERE...."
Thanks.

You need to use the CASE expression, like so:
UPDATE tableB
SET col1 =
CASE WHEN (0, 0) = (SELECT col1, col2
FROM tableA
WHERE <tableA constraints>
)
THEN "this value"
ELSE "that value"
END
WHERE <tableB constraints>;
Note also the case is using the (a,b,c) = (select A, B, C ...) syntax, which is a handy, but underused sql feature = you can compare not only single values (scalars) but multiple values (vectors).

Why not write whatever logic you need in a stored procedure/package and call that from .NET?

The best thing to do this is with stored procedures. If you have other sql commands or what ever, then use transactions: run as stored procedures as you want intercalated with data that does not come from database, the commit (or rollback).
If something changes the stored procedures becomes invalid on text-sql does not happens, you have to wait to unit-tests o runtime error. We use generated code that makes very easy to call stored procedures (class.method).
DISCLAIMER: I'm not an stored procedure fanatic. I use Stored Procedures and ORM as best fits on each moment. It depends on the case.

Related

How does SQL Server handle an update with no change?

I want to run an update statement using a case statement. 99% of time there will be no change. So I was wondering about the performance.
For example if I run
update <table>
set field1 = field1
does it copy field1 and then write it into field1 or does it do nothing?
That depends on the database and in SQL Server, the answer is yes.
You should filter the values instead. Instead of:
update t
set x = (case when a = b then c else x end);
You should do:
update t
set x = c
where a = b;

How to use If/Else or Case statement for a checkbox with in a Select Query in SQL

I have a check box on front end.
If the check box is checked: data with only checked chk1 should appear on front end.
If the check box is not checked: full data should appear on front end.
Please suggest how should I proceed with the same in SQL Using If else / Case statement.
I am using:
SELECT *
FROM table1
where (some conditions) AND
CASE #a.Id_Oem_Irm
WHEN 0 THEN (a.id_oem_irm in(0,1))
WHEN 1 THEN (a.id_oem_irm in (1))
END
PS: a.Id_Oem_Irm: a is the table name, Id_oem_irm is the column name for check box.
I would recommend writing this as:
where (some conditions) AND
((#a.Id_Oem_Irm = 0 and a.id_oem_irm in(0, 1)) OR
(#a.Id_Oem_Irm = 1 and a.id_oem_irm in (1) )
)
I am not sure what #a.Id_Oem_Irm is supposed to be. I suspect you want a variable there.
Or you could tune it a bit like this:
SELECT *
FROM table1
where (some conditions) AND
(a.id_oem_irm = 1 OR
#a.Id_Oem_Irm = 0)
This is only valid if #a.Id_Oem_Irm is always 0 or 1. Otherwise, you obviously should add a #a.Id_Oem_Irm IN (0,1) or <= 1 (if it can't be negative) condition.
What is a? The alias for another table you didn't include here?
The simpler way to do this is create a storedprocedure which will take the input "#a.Id_Oem_Irm"
Example:
CREATE PROCEDURE p_get_a_data(
IN Id INT(1)
)
BEGIN
IF (Id = 0) BEGIN
--YOur query to retrieve partiular row/ column
END;
IF(Id = 1) BEGIN
--YOur query to retrieve all data
SELECT * FROM table1
where (some conditions)
END;
END
The usage of stored procedure will give you selected data and the query is precompiled in the database so it faster.
Note: I didn't quite understand what your query is returning so replace mine with yours

Return result of select query only if not empty

Working with Sql Server. Writing a stored procedure. Here is the pseudocode for what I want to achieve:
IF EXISTS ( SELECT field1
FROM t1
WHERE field1 = ... AND field2 = ...)
BEGIN
SELECT field1
FROM t1
WHERE field1 = ... AND field2 = ...
END
any better way of doing this? Any help appreciated.
Chirayu
Update: The problem is that the same query is executed twice. I cannot also just the run query once and return null (if the result is null i would like to return an alternative result).
I have done this before using a CTE and table variable, it requires more lines of code but the query is only written once, therefore your logic exists in a single place.
DECLARE #Results TABLE (Result INT);
WITH ResultsCTE AS
(
--Your query goes here
SELECT 1 as Result
WHERE 1 = 1
)
INSERT INTO #Results
SELECT Result
FROM ResultsCTE
IF (SELECT COUNT(*) FROM #Results) > 0
BEGIN
SELECT * FROM #Results
END
ELSE BEGIN
SELECT 'Do Something Else or Do Nothing!'
END
You could check ##ROWCOUNT after running the query once to determine whether or not to return the value:
http://msdn.microsoft.com/en-us/library/ms187316.aspx
If the select doesn't yield any results, no results will be returned. I don't see any reason to use a condition here, unless I'm missing something...
A stored procedure that sometimes returns a result while sometimes it doesn't would be a nightmare to use from any API. The client side API has different entry points depending on whether you return a result set (SqlCommand.ExecuteReader) or it does not return a result set (SqlCommand.ExecuteNonQuery). It would be impossible for the application to know ahead of time which API to use! Modeling tools use the SET FMTONLY option to analyze the metadata of returned result sets and the modeling tools are very confused when your returned result set start changing shape at random. In other words, you are down the wrong path, stop and turn around.
Just run the query, it no rows match your criteria it will simply return an empty result set. Which is exactly what every client API and modeling tool expects from your procedure.

SQL Server 2005 Stored Procedure Using Table Variable Doesn't Return ResultSet to VB6 App

I have a stored procedure which selects a single field from a single row (based on a conditional statement) and stores it in a local variable. Based on the value of this field I update the same field in the same row of the table (using the same conditional statement). So the procedure first does a select into a local variable and then later updates the same field of the same row. The procedure then returns a result set via a select on a table variable (I have also attempted using a temporary table). The result set does not contain the variable or field I have updated. It doesn't even include any fields from this table.
The procedure works correctly when called from either Management Studio or from a test C# application. However, when called from my VB6 app the result set is not returned. All database updates are still performed however.
I have tried writing the stored procedure with a transaction and without, with TRY...CATCH and without, and both at the same time. I've tried various combinations of transaction isolation. No exceptions are thrown and the transaction will always commit. I've also used the WITH (NOLOCK) hint on the select statement. If I leave out the table update it will work. If I leave out the assignment to a local variable and instead hard code a value it works. If I simply use the select where I would put the variable it will NOT work.
Interestingly, if I add some random select statement to the procedure it will return that result set. I can even select that same field from the same record I assign to my variable with no issue. But it still will not return my desired result set.
My result set is a select from a table variable which is populated via insert statements using variables set throughout the procedure. There are no table joins at all. I do pass 2 parameters to the procedure - one of which is used in my conditional statement in the original select. But I still get the same behavior when I omit both parameters and hard code values.
I have tried restarting my SQL Server (2005 version 9.0.4053), restarting my machine, i have tried with NOCOUNT ON and OFF, I'm basically out of ideas.
Edit - Details of VB call and stored procedure signature:
I'll try to give as good as a description as I can without publishing actual code. I'm actually posting this for another developer who works with me, so please bear with me.
VB6 Call:
With cmdCommand
.ActiveConnection = cnnConn
.CommandType = adCmdStoredProc
.CommandText = "uspMyStoredProcedure"
.Parameters("#strParam1") = strFunctionParameter1
.Parameters("#bolParam2") = bolFunctionParameter2
.Execute
End With
MyResultSet.CursorLocation = adUseClient
MyResultSet.Open cmdCommand, , adOpenStatic, adLockReadOnly
Stored Procedure signature:
CREATE PROCEDURE uspMyStoredProcedure
#strParam1 NVARCHAR(XX),
#bolParam2 BIT
AS
BEGIN
SET NO COUNT ON
DECLARE #var1 NVARCHAR(XX),
#var2 NVARCHAR(XX),
#var3 NVARCHAR(XX),
#var4 INT,
#var5 BIT
--DECLARATION OF OTHER VARIABLES
DECLARE #varTableVariable TABLE
(
strTblVar1 NVARCHAR(XX) ,
intTblVar2 INT ,
strTblVar3 NVARCHAR(XX) ,
bolTblVar4 BIT ,
datTblVar5 DATETIME
)
SELECT #var1 = t.Field1, #var2 = t.Field2
FROM Table1 t
WHERE t.ID = #strParam1
SELECT #var3 = t2.Field1
FROM Table2 t2
IF (Condition)
BEGIN
SET #var4 = 1
IF (Condition)
BEGIN
--SET SOME VARIABLES
END
ELSE
BEGIN
UPDATE TABLE1
SET Field3 = #var4
WHERE Field1 = #strParam1
END
END
ELSE
BEGIN
IF(Condition)
BEGIN
SELECT #var5 = ISNULL(Condition)
FROM Table3 t3
WHERE t3.Field = #strParam1
--SET SOME MORE VARIABLES
END
END
IF(Condition)
BEGIN
UPDATE Table1
SET Field5 = #SomeVariable
WHERE Field1 = #strParam1
END
INSERT INTO Table4 (Field1, Field2, Field3)
SELECT #SomeVar1, #someVar2, #SomeVar3
FROM SomeOtherTable
WHERE Field3 = #someVariable
IF(Condition)
BEGIN
INSERT INTO #varTableVariable (strTblVar1, intTblVar2,
strTblVar3, bolTblVar4, datTblVar5 )
VALUES (#SomeVar1, #SomeVar2, #SomeVar3, #SomeVar4, #SomeVar5)
END
SELECT *
FROM #varTableVariable
END
So, essentially, the procedure takes two parameters. It carries out a number of simple operations - inserting and selecting data from a couple of different tables, an update to a table and inserting a row into a table variable.
The procedure finishes with a select from the table variable. There's nothing fancy about the procedure, or the call from VB6. As previously stated, the behaviour observed is unusual in that by commenting out certain sections the call and return will work - data is returned. Calling the same procedure from a C#.NET test app works and successfully returns the desired result.
All we manage to get back in the VB6 app is an empty recordset.
Edit 2: We've just found out that if we create an arbitrary table to hold the data to be returned by the final select statement instead of using a table variable, the procedure works...
We discovered that the stored procedure was actually executing twice, due to the way it was being called from VB6:
With cmdCommand
.ActiveConnection = cnnConn
.CommandType = adCmdStoredProc
.CommandText = "uspMyStoredProcedure"
.Parameters("#strParam1") = strFunctionParameter1
.Parameters("#bolParam2") = bolFunctionParameter2
.Execute
End With
MyResultSet.CursorLocation = adUseClient
MyResultSet.Open cmdCommand, , adOpenStatic, adLockReadOnly
The command object 'cmdCommand' is executed the first time with the explicit call as the final line in the 'With' statement, '.Execute'.
What we found was that the last line: 'MyResultSet.Open cmdCommand...' is also implicitly executing the stored procedure a second time.
Since the stored procedure's function is essentially to activate and deactivate an alarm, by executing twice we were getting the activation and deactivation occurring at once and therefore no resultset returned.
Hopefully this might help avoid someone else getting stuck on something like this.

Setting multiple scalar variables from a single row in SQL Server 2008?

In a trigger, I have code like:
SET #var1 = (SELECT col1 FROM Inserted);
SET #var2 = (SELECT col2 FROM Inserted);
Is it possible to write the above in a single line? Something conceptually like:
SET (#var1,#var2) = (SELECT col1,col2 FROM Inserted);
Obviously I tried the above, without success; am I just stuck with the first method?
Even if possible, is that a good idea?
Thanks!
yes, use first method.
Or...
SELECT
#var1 = col1
,#var2 = col2
FROM
Inserted;
However, it is a major red flag if you are expecting to set variable values like that in a trigger. It generally means the trigger is poorly designed and needs revision. This code expects there will be only one record in inserted and this is something that is not going to be true in all cases. A multiple record insert or update will have multiple records in inserted and the trigger must account for that (please without using a trigger!!!). Triggers should under no circumstances be written to handle only one-record inserts/updates or deletes. They must be written to handle sets of data.
Example to insert the values from inserted to another table where the trigger is on table1:
CREATE TRIGGER mytrigger on table1
AFTER INSERT
AS
INSERT table2 (field1, field2, field3)
SELECT field1, 'test', CASE WHEN field3 >10 THEN field3 ELSE 0 END
FROM inserted
No, it is not possible. SET accepts a single target and value. AFAIK.