Why is my LINQ stored procedure returning -1 instead of a proper number every time? - linq

In the database I have a procedure that returns different numerical values ​​with the word RETURN.
This procedure I use with LINQ in my application, but it always returns -1 instead of the proper number.
Example T-SQL:
Create PROCEDURE EmailStatus
AS
BEGIN
IF NOT EXISTS (SELECT * FROM msdb.sys.service_queues WHERE name = N'ExternalMailQueue'
AND is_receive_enabled = 1)
return (100)
ELSE
RETURN 101
END
Example LINQ:
TestXMLEntities nw = new TestXMLEntities();
var r = nw.EmailStatus();

Use SELECT 101 instead of RETURN 101. Description

Related

Oracle PL/SQL seems to ignore WHERE clause of my Function

I have a table of club´s members with a column of status that a want to use as a filter in my function below. But the result is always the same no matter what number I pass as a function parameter. The count always comes with the total registers of the entire table.
CREATE OR REPLACE FUNCTION fn_count_members
(id_status in club.members.id_status%TYPE)
RETURN NUMBER IS
total club.members.id%TYPE:=0;
BEGIN
SELECT COUNT(id) INTO total
FROM club.members m
WHERE m.id_status = id_status;
RETURN total;
END;
And the query:
select id_status, fn_count_members(1) as total from club.members;
What am I missing here?
Thank you.
You have two issues:
CREATE OR REPLACE FUNCTION fn_count_members(
p_id_status in club.members.id_status%TYPE -- change the parameter name to
-- something different from the
-- column name.
)
RETURN NUMBER IS
total club.members.id%TYPE:=0;
BEGIN
SELECT COUNT(id) INTO total
FROM club.members m
WHERE m.id_status = p_id_status; -- and here
RETURN total;
END;
/
Then you need to pass the parameter in rather than using 1:
select id_status,
fn_count_members(id_status) as total
from club.members;
db<>fiddle here
This is wrong:
(id_status in club.members.id_status%TYPE)
---------
this
Because, when used in where clause
WHERE m.id_status = id_status
it turns out to be something like where 1 = 1 and returns everything, no filtering at all.
Rename parameter to e.g.
(par_id_status in club.members.id_status%TYPE)
and use it as
WHERE m.id_status = par_id_status

basic variables returning all results

I have the below variable.
DECLARE
v_clt NUMBER;
BEGIN
DBMS_OUTPUT.PUT_LINE(v_clt);
END;
/
SELECT *
FROM CCP
WHERE CCP.ID = &v_clt
--AND CASE WHEN &v_clt < 1 THEN ID ELSE &v_clt END ID
I have may sub-queries within my query and I would like to be able to test the query by adding in one value for each sub queries, hence the variable and it works.
I also would like to be able to return all results at any given point too. Can this be accomplished?
You can use special value as 0 combine with decode (or case) to view all results
WHERE CCP.ID = decode(&v_clt,0,CCP.ID,&v_clt )

SQL%FOUND where SELECT query returns no rows

I have the following function, which returns the next available client ID from the Client table:
CREATE OR REPLACE FUNCTION getNextClientID RETURN INT AS
ctr INT;
BEGIN
SELECT MAX(NUM) INTO ctr FROM Client;
IF SQL%NOTFOUND THEN
RETURN 1;
ELSIF SQL%FOUND THEN
-- RETURN SQL%ROWCOUNT;
RAISE_APPLICATION_ERROR(-20010, 'ROWS FOUND!');
-- RETURN ctr + 1;
END IF;
END;
But when calling this function,
BEGIN
DBMS_OUTPUT.PUT_LINE(getNextClientID());
END;
I get the following result:
which I found a bit odd, since the Client table contains no data:
Also, if I comment out RAISE_APPLICATION_ERROR(-20010, 'ROWS FOUND!'); & log the value of SQL%ROWCOUNT to the console, I get 1 as a result.
On the other hand, when changing
SELECT MAX(NUM) INTO ctr FROM Client;
to
SELECT NUM INTO ctr FROM Client;
The execution went as expected. What is the reason behind this behavior ?
Aggregate functions will always return a result:
All aggregate functions except COUNT(*), GROUPING, and GROUPING_ID
ignore nulls. You can use the NVL function in the argument to an
aggregate function to substitute a value for a null. COUNT and
REGR_COUNT never return null, but return either a number or zero. For
all the remaining aggregate functions, if the data set contains no
rows, or contains only rows with nulls as arguments to the aggregate
function, then the function returns null.
You can change your query to:
SELECT COALESCE(MAX(num), 1) INTO ctr FROM Client;
and remove the conditionals altogether. Be careful about concurrency issues though if you do not use SELECT FOR UPDATE.
Query with any aggregate function and without GROUP BY clause always returns 1 row. If you want no_data_found exception on empty table, add GROUP BY clause or remove max:
SQL> create table t (id number, client_id number);
Table created.
SQL> select nvl(max(id), 0) from t;
NVL(MAX(ID),0)
--------------
0
SQL> select nvl(max(id), 0) from t group by client_id;
no rows selected
Usually queries like yours (with max and without group by) are used to avoid no_data_found.
Agregate functions like MAX will always return a row. It will return one row with a null value if no row is found.
By the way SELECT NUM INTO ctr FROM Client; will raise an exception where there's more than one row in the table.
You should instead check whether or not ctr is null.
Others have already explained the reason why your code isn't "working", so I'm not going to be doing that.
You seem to be instituting an identity column of some description yourself, probably in order to support a surrogate key. Doing this yourself is dangerous and could cause large issues in your application.
You don't need to implement identity columns yourself. From Oracle 12c onwards Oracle has native support for identity columns, these are implemented using sequences, which are available in 12c and previous versions.
A sequence is a database object that is guaranteed to provide a new, unique, number when called, no matter the number of concurrent sessions requesting values. Your current approach is extremely vulnerable to collision when used by multiple sessions. Imagine 2 sessions simultaneously finding the largest value in the table; they then both add one to this value and try to write this new value back. Only one can be correct.
See How to create id with AUTO_INCREMENT on Oracle?
Basically, if you use a sequence then you don't need any of this code.
As a secondary note your statement at the top is incorrect:
I have the following function, which returns the next available client ID from the Client table
Your function returns the maximum ID + 1. If there's a gap in the IDs, i.e. 1, 2, 3, 5 then the "missing" number (4 in this case) will not be returned. A gap can occur for any number of reasons (deletion of a row for example) and does not have a negative impact on your database in any way at all - don't worry about them.

Performance of User Defined Functions in SQL Server 2000 and 2005

We have two tables, Customer and CustomerEvent both contains few million rows. On SQL Server 2000, we deployed an UDF called fn_CustomerEvent which returns TRUE or FALSE based on two parameters CustomerID and EventCode, e.g.
SELECT dbo.fn_CustomerEvent(1345678, 'Music')
The UDF code is:
CREATE FUNCTION [dbo].[fn_CustomerEvent](#CustomerID INT, #EviCode NVARCHAR(10))
RETURNS NVARCHAR(10)
AS
BEGIN
DECLARE #List NVARCHAR(10)
SELECT #List = CASE
WHEN COUNT(*) > 0 THEN 'TRUE'
ELSE 'FALSE'
END
FROM CustomerEvent
WHERE
CustomerID = #CustomerID
AND EviCode = #EviCode
RETURN #List
END
The performance on SQL Server 2000 was great. Return TOP 5000 rows within 3 seconds. For example,
SELECT TOP 5000
CustomerID, dbo.fn_CustomerEvent(1345678, 'Music')
FROM [Table1]
But now, we are moving to SQL Server 2005. Same code, same UDF, but performance drops dramatically from 3 seconds to 1 minutes 20 seconds.
Can anyone point me a right direction on where should I start to optimize the performance?
The scalar UDF is evaluated for each row (i.e. 5000 times). You could either call it once and store the result in a variable
DECLARE #Result nvarchar(10)
SELECT #Result = dbo.fn_CustomerEvent(1345678, 'Music')
SELECT TOP 5000
CustomerID, #Result
FROM [Table1]
or you can use an inline TVF (and I would also use EXISTS instead of COUNT)
CREATE FUNCTION CustomerEvent (#CustomerID INT,
#EviCode NVARCHAR(10))
RETURNS TABLE
AS
RETURN
(SELECT CASE
WHEN EXISTS(SELECT *
FROM CustomerEvent
WHERE CustomerID = #CustomerID
AND EviCode = #EviCode) THEN 'TRUE'
ELSE 'FALSE'
END)
See Scalar functions, inlining, and performance: An entertaining title for a boring post for more about this technique.
There is one big problem with UDF's: they don't work with indexes. If you want to get code re-use and maintain performance, I will normally build either a computed column (which can be indexed) or a view.
CREATE FUNCTION CustomerEvent (#CustomerID INT,
#EviCode NVARCHAR(10))
RETURNS TABLE
AS
RETURN
(SELECT COALESCE((SELECT 'TRUE' FROM CustomerEvent
WHERE
CustomerID = #CustomerID
AND EviCode = #EviCode)
, 'FALSE'))
Check for Indexes, Rebuild Them and Update your statistics.

Oracle Cursor Is Not Working as Expected

Here is the procedure,
CREATE OR REPLACE PROCEDURE provsnXmlCmprsn (
encyNo SAS_PRO_CTL.AGENCYNO%TYPE, period SAS_PRO_CTL.PERIODE%TYPE) IS
xmlContent SAS_PRO_XML.XMLCONTENT%TYPE;
sasProvisionId SAS_PRO_CTL.SASPROVISIONID%TYPE;
CURSOR crsrXml IS
SELECT XMLCONTENT, c.SASPROVISIONID FROM SAS_PRO_XML x, SAS_PRO_CTL c
WHERE x.SASPROVISIONID = c.SASPROVISIONID AND c.PERIODE = period
AND c.AGENCYNO = agencyNo ORDER BY XMLLINENO;
BEGIN
DBMS_OUTPUT.put_line('Params: ' || agencyNo || ', ' || period);
OPEN crsrXml;
LOOP
FETCH crsrXml INTO xmlContent, sasProvisionId;
EXIT WHEN crsrXml%NOTFOUND;
DBMS_OUTPUT.put_line('XML Content Length: ' || LENGTH(xmlContent));
END LOOP;
CLOSE crsrXml;
END provsnXmlCmprsn;
The query in the cursor is retrieving 5 rows, whereas 1 row is expected, according to the condition and argument values. The same query results in 1 row, when run independently. And the surprising part is, the query in the cursor always return 5 rows no matter if the condition, c.PERIODE = period AND c.AGENCYNO = agencyNo, passed or not. Which clearly means that this query,
SELECT XMLCONTENT, c.SASPROVISIONID FROM SAS_PRO_XML x, SAS_PRO_CTL c
WHERE x.SASPROVISIONID = c.SASPROVISIONID AND c.PERIODE = period
AND c.AGENCYNO = agencyNo ORDER BY XMLLINENO;
and this query,
SELECT XMLCONTENT, c.SASPROVISIONID FROM SAS_PRO_XML x, SAS_PRO_CTL c
WHERE x.SASPROVISIONID = c.SASPROVISIONID ORDER BY XMLLINENO;
are behaving same inside the cursor. This, AND c.PERIODE = period AND c.AGENCYNO = agencyNo, part is not at all considered. Any idea what's going wrong?
One of your parameters has the same name as the column: AGENCYNO. Because of the way scoping works this evaluates to 1=1. This is why it is good practice to give parameters unique names, for example by prepending them with p_.
You should find that
AND c.PERIODE = p_period AND c.AGENCYNO = p_agencyNo
returns the desired one row. Strictly speaking you don't need to change the name of period to p_period because it is already distinguished from periode. But consistency is a virtue in software engineering.

Resources