SQL - Why does the ordering matter for this select? - sql

I ran the below code and got the following error;
Conversion failed when converting the varchar value '1.5' to data type int
BEGIN -- first update to check Interface held SMR and Interface held SMA
update interface set INTERR = 'U7'
from interface i
where
i.conttype = 'SMR'
and isnumeric(i.contrate)=1
and cast(i.contrate as decimal(12,2)) < 5
and caseno = #caseno
and exists (
select 1
from interface i2
where i2.caseno = #caseno
and i2.conttype = 'SMA'
and i2.intmembno = i.intmembno
and i2.effdte = i.effdte
and i2.contrate > cast(0 as decimal(12,2))
and isnumeric(i2.contrate)=1
)
In this example SMR = 5 and SMA = 1.5 and both values have been declared as Numerics. However by switching around the ordering of the clauses the error stops occuring and the stored procedure continues on as it should (see below)
BEGIN -- first update to check Interface held SMR and Interface held SMA
update interface set INTERR = 'U7'
from interface i
where
i.conttype = 'SMR'
and isnumeric(i.contrate)=1
and cast(i.contrate as decimal(12,2)) < 5
and caseno = #caseno
and exists (
select 1
from interface i2
where i2.caseno = #caseno
and isnumeric(i2.contrate)=1 -- This was moved up
and i2.conttype = 'SMA'
and i2.intmembno = i.intmembno
and i2.effdte = i.effdte
and i2.contrate > cast(0 as decimal(12,2))
)
Can you help me understand why the ordering matters? Normally it doesn't and shouldn't, as far as I know.
Thanks!
Paul

WHERE conditions are not executed in any particular order. This is even true when using subqueries and CTEs -- the optimizer rearranges processing and for good reason.
And, using implicit conversion is dangerous -- as you are finding. So, use explicit conversions and do:
where i.conttype = 'SMR' and
try_cast(i.contrate as decimal(12,2)) < 5 and
caseno = #caseno and
exists (select 1
from interface i2
where i2.caseno = #caseno and
i2.conttype = 'SMA' and
i2.intmembno = i.intmembno and
i2.effdte = i.effdte and
try_cast(i2.contrate as decimal(12,2)) > 0
)
Notes:
You do not have to check isnumeric().
There is no need to cast a constant such as 0 for the comparison.
You can do similar things with a case in pre-2012 versions of SQL Server.

Related

GETTING ERROR-- ORA-00936:MISSING EXPRESSION for below query please help on this

SELECT CASE (SELECT Count(1)
FROM wf_item_activity_statuses_v t
WHERE t.activity_label IN ('WAITING_DISB_REQ',
'LOG_DDE',
'LOG_SENDBACK_DDE')
AND t.item_key IN(
SELECT r.i_item_key
FROM wf_t_item_xref r
WHERE r.sz_appl_uniqueid = '20400000988')
)
WHEN 0 THEN
(
delete
from t_col_val_document_uploaded p
WHERE p.sz_application_no = '20400000988'
AND p.sz_collateral_id = 'PROP000000000PRO1701'
AND p.i_item_key = '648197'
AND p.i_document_srno = '27' )
WHEN 1 THEN
(
DELETE
FROM t_col_val_document_uploaded p
WHERE p.sz_application_no = '20400000988'
AND p.sz_collateral_id = 'PROP000000000PRO1701'
AND p.i_document_srno = '28' )
ELSE NULL
END
FROM dual;
You need to recreate your query and make sure to follow the flow of the clauses properly, please check the next two links to get a better understanding :
[ORA-00936: missing expression tips]
How do I address this ORA-00936 error?
Answer: The Oracle oerr utility notes this about the ORA-00936 error:
ORA-00936 missing expression
Cause: A required part of a clause or expression has been omitted. For example, a SELECT statement may have been entered without a list of columns or expressions or with an incomplete expression. This message is also issued in cases where a reserved word is misused, as in SELECT TABLE.
Action: Check the statement syntax and specify the missing component.
The ORA-00936 happens most frequently:
1 - When you forget list of the column names in your SELECT statement.
2. When you omit the FROM clause of the SQL statement.
ora-00936-missing-expression
I hope this can help you.
You cannot use a simple select query like this. You have to use a PL/SQL block like below -
DECLARE NUM_CNT NUMBER := 0;
BEGIN
SELECT Count(1)
INTO NUM_CNT
FROM wf_item_activity_statuses_v t
WHERE t.activity_label IN ('WAITING_DISB_REQ',
'LOG_DDE',
'LOG_SENDBACK_DDE')
AND t.item_key IN(SELECT r.i_item_key
FROM wf_t_item_xref r
WHERE r.sz_appl_uniqueid = '20400000988');
IF NUM_CNT = 0 THEN
delete
from t_col_val_document_uploaded p
WHERE p.sz_application_no = '20400000988'
AND p.sz_collateral_id = 'PROP000000000PRO1701'
AND p.i_item_key = '648197'
AND p.i_document_srno = '27';
ELSIF NUM_CNT = 1 THEN
DELETE
FROM t_col_val_document_uploaded p
WHERE p.sz_application_no = '20400000988'
AND p.sz_collateral_id = 'PROP000000000PRO1701'
AND p.i_document_srno = '28' )
END IF;
END;

Case expression with Boolean from PostgreSQL to SQL Server

I am translating a query from PostgreSQL to SQL Server. I didn't write the query in PostgreSQL and it's quite complicated for my knowledge so i don't understand every piece of it.
From my understand: we are trying to find the max version from p_policy and when insurancestatus = 7 or 14 / transactiontype = CAN, we compare two dates (whose format are BIG INT).
This is the PG Query:
SELECT *
FROM BLABLABLA
WHERE
pol.vnumber = (
SELECT MAX(pol1.vnumber)
FROM p_policy pol1
AND ( CASE WHEN pol1.insurancestatus IN (7,14)
or pol1.transactiontype IN ('CAN')
-- ('CAN','RCA')
THEN pol1.veffectivedate = pol1.vexpirydate
ELSE pol1.veffectivedate <> pol1.vexpirydate
END
)
AND pol1.vrecordstatus NOT IN (30,254)
etc.
I am used to have a where statement where I compare it to a value. I understand here from the Case statement we will have a boolean, but still that must be compared to something?
Anyway the main purpose is to make it work in SQL, but I believe SQL can't read a CASE statement where THEN is a comparison.
This is what I tried:
SELECT *
FROM BLABLABLA
WHERE pol.vnumber =
(
SELECT MAX(pol1.vnumber)
FROM p_policy pol1
WHERE sbuid = 4019
AND ( CASE WHEN pol1.insurancestatus IN (7,14)
or pol1.transactiontype IN ('CAN')
THEN CASE
WHEN pol1.veffectivedate = pol1.vexpirydate THEN 1
WHEN pol1.veffectivedate <> pol1.vexpirydate THEN 0
END
END
)
AND pol1.vrecordstatus NOT IN (30,254)
etc.
And then I get this error from SQL Server (which directly the last line of the current code - so after the double case statement)
Msg 4145, Level 15, State 1, Line 55
An expression of non-boolean type specified in a context where a condition is expected, near 'AND'.
Thank you !Let me know if it is not clear
I think you want boolean logic. The CASE expression would translate as:
(
(
(pol1.insurancestatus IN (7,14) OR pol1.transactiontype = 'CAN')
AND pol1.veffectivedate = pol1.vexpirydate
) OR (
NOT (pol1.insurancestatus IN (7,14) OR pol1.transactiontype = 'CAN')
AND pol1.veffectivedate <> pol1.vexpirydate
)
)
There are 2 main issues with your snippet, SQL Server-syntax-wise.
SELECT * FROM BLABLABLA WHERE
pol.vnumber = /* PROBLEM 1: we haven't defined pol yet; SQL Server has no idea what pol.vnumber is here, so you're going to get an error when you resolve your boolean issue */
(
SELECT MAX(pol1.vnumber)
FROM p_policy pol1
WHERE sbuid = 4019
AND ( CASE WHEN pol1.insurancestatus IN (7,14)
or pol1.transactiontype IN ('CAN')
THEN CASE
WHEN pol1.veffectivedate = pol1.vexpirydate THEN 1
WHEN pol1.veffectivedate <> pol1.vexpirydate THEN 0
END
END
) /* PROBLEM 2: Your case statement returns a 1 or a 0..
which means your WHERE is saying
WHERE sbuid = 4019
AND (1)
AND pol1.vrecordstatus NOT IN (30,254)
SQL Doesn't like that. I think you meant to add a boolean operation using your 1 or 0 after the parenthesis.
like this: */
= 1
AND pol1.vrecordstatus NOT IN (30,254)

SQL function not working properly, but does when not as a function

I'm having another issue with a SQL Function that I had written. If I run it not in a function and use declared variables, then it works perfectly. But as soon as I put it into a function and run it, nothing appears, its empty.
I cannot put this into a stored procedure, it needs to be in a function.
the code is
select * from [MYTABLE]
where MajorGroupID = #MajorGroupID
and ((#Status = 0 and (
Inactive = 0)
))
or MajorGroupID = #MajorGroupID and (#Status = 1 and (Inactive = 0 or Inactive = 1))
I am not really familiar with functions, I can do basic things with functions but when it comes to adding logic to it. If I was allowed to use stored procedures then I wouldn't be having problems.
This is MSSQL and using SQL Server 2010.
EDIT, Added complete function
CREATE FUNCTION [dbo].[WT_FN_GET_MYTABLE_By_MajorGroupID_Inactive]
(
#MajorGroupID varchar,
#Status int
)
RETURNS TABLE
AS
RETURN
select * from [MYTABLE]
where MajorGroupID = #MajorGroupID
and ((#Status = 0 and (
Inactive = 0)
))
or MajorGroupID = #MajorGroupID and (#Status = 1 and (Inactive = 0 or Inactive = 1))
You don't provide a length for #MajorGroupID varchar, so it is going to be 1 by default, at which point it will not find anything in the table.
Provide a length, e.g. #MajorGroupID varchar(30).

Error converting data type varchar to numeric CAST not working

I know this has been beaten like a dead horse. However no matter how I slice it, cast it or convert it I have the same issue.
Error converting data type varchar to numeric.
SELECT property_id, property_case_number, property_address, property_city,
property_state, property_zip, property_lon, property_lat
FROM property
WHERE (property_active = 1)
AND
(property_county = (SELECT property_county FROM property AS property_1
WHERE (property_id = 9165)))
AND
(property_id <> 9165)
AND
property_lon IS NOT Null
AND
property_lat IS NOT Null
AND
dbo.LatLonRadiusDistance(
CONVERT(DECIMAL(15,12),(select property_lat from property where property_id = 9165)),
CONVERT(DECIMAL(15,12),(select property_lon from property where property_id = 9165)),
property_lat,property_lon) <= '5'
I run into this issue as soon as I add dbo.LatLonRadiusDistance at the end.
dbo.LatLonRadiusDistance compares lat & lon distance in miles.
FUNCTION [dbo].[LatLonRadiusDistance]
(
#lat1Degrees decimal(15,12),
#lon1Degrees decimal(15,12),
#lat2Degrees decimal(15,12),
#lon2Degrees decimal(15,12)
)
RETURNS decimal(9,4)
AS
BEGIN
DECLARE #earthSphereRadiusKilometers as decimal(10,6)
DECLARE #kilometerConversionToMilesFactor as decimal(7,6)
SELECT #earthSphereRadiusKilometers = 6366.707019
SELECT #kilometerConversionToMilesFactor = .621371
-- convert degrees to radians
DECLARE #lat1Radians decimal(15,12)
DECLARE #lon1Radians decimal(15,12)
DECLARE #lat2Radians decimal(15,12)
DECLARE #lon2Radians decimal(15,12)
SELECT #lat1Radians = (#lat1Degrees / 180) * PI()
SELECT #lon1Radians = (#lon1Degrees / 180) * PI()
SELECT #lat2Radians = (#lat2Degrees / 180) * PI()
SELECT #lon2Radians = (#lon2Degrees / 180) * PI()
-- formula for distance from [lat1,lon1] to [lat2,lon2]
RETURN ROUND(2 * ASIN(SQRT(POWER(SIN((#lat1Radians - #lat2Radians) / 2) ,2)
+ COS(#lat1Radians) * COS(#lat2Radians) * POWER(SIN((#lon1Radians - #lon2Radians) / 2), 2)))
* (#earthSphereRadiusKilometers * #kilometerConversionToMilesFactor), 4)
END
I'm sure it's something to do with
(select property_lat from property where property_id = 9165)
But no matter how I cast or convert it doesn't change things.
And if I run the function by itself it doesn't give an error.
Anyone have any insights?
here is a sample row
8462 023-125514 15886 W MOHAVE ST GOODYEAR AZ 85338-0000 -112.400297000000 33.429041000000
property_lat & property_lon are varchar(50)
Most likely you are expecting boolean short circuit to rescue the order of evaluating your WHERE clause. This is a known fallacy: boolean operator short circuit is not guaranteed in SQL. See On SQL Server boolean operator short-circuit for a discussion and proof that boolean short circuit can be skipped by query optimizer. Similar topic is T-SQL functions do no imply a certain order of execution. The gist of it is that SQL is a declarative language, not an imperative one.
In your case probably your cast and converts will be called for properties with IDs different from property_id = 9165 and property_active=1 and may attempt to cast string values that are not numerics to a numeric, hence the exception you see. Is difficult to give a precise diagnosis since so much information is missing from your problem description (like the exact definition of all object involved, including all tables, indexes, column types etc).
Your best avenue is to upgrade to SQL Server 2008 and use the built in geography type which has built-in support for STDistance:
This is a close approximate to the geodesic distance. The deviation of
STDistance() on common earth models from the exact geodesic distance
is no more than .25%.
After playing with the query I got it working.
SELECT [property_id], [property_case_number], [property_address], [property_city],
[property_state], [property_zip], [property_lon],
[property_lat]
FROM property
WHERE ([property_active] = 1)
AND
([property_county] = (SELECT b.property_county FROM property b WHERE (b.property_id = #prop_id)))
AND
([property_id] <> #prop_id)
AND
[property_lon] IS NOT Null
AND
[property_lat] IS NOT Null
AND
dbo.LatLonRadiusDistance(
(SELECT c.property_lat FROM property c WHERE (c.property_id = #prop_id)),
(SELECT d.property_lon FROM property d WHERE (d.property_id = #prop_id)),
CAST([property_lat] as FLOAT),
CAST([property_lon] as FLOAT)) <= 5
adding the [] seems to have skirted the issue I was having.

Stored procedure to find next and previous row in SQL Server 2005

Right now I have this code to find next and previous rows using SQL Server 2005. intID is the gallery id number using bigint data type:
SQL = "SELECT TOP 1 max(p.galleryID) as previousrec, min(n.galleryID) AS nextrec FROM gallery AS p CROSS JOIN gallery AS n where p.galleryid < '"&intID&"' and n.galleryid > '"&intID&"'"
Set rsRec = Server.CreateObject("ADODB.Recordset")
rsRec.Open sql, Conn
strNext = rsRec("nextrec")
strPrevious = rsRec("previousrec")
rsRec.close
set rsRec = nothing
Problem Number 1:
The newest row will return nulls on the 'next record' because there is none. The oldest row will return nulls because there isn't a 'previous record'. So if either the 'next record' or 'previous record' doesn't exist then it returns nulls for both.
Problem Number 2:
I want to create a stored procedure to call from the DB so intid can just be passed to it
TIA
This will yield NULL for previous on the first row, and NULL for next on the last row. Though your ordering seems backwards to me; why is "next" lower than "previous"?
CREATE PROCEDURE dbo.GetGalleryBookends
#GalleryID INT
AS
BEGIN
SET NOCOUNT ON;
;WITH n AS
(
SELECT galleryID, rn = ROW_NUMBER()
OVER (ORDER BY galleryID)
FROM dbo.gallery
)
SELECT
previousrec = MAX(nA.galleryID),
nextrec = MIN(nB.galleryID)
FROM n
LEFT OUTER JOIN n AS nA
ON nA.rn = n.rn - 1
LEFT OUTER JOIN n AS nB
ON nB.rn = n.rn + 1
WHERE n.galleryID = #galleryID;
END
GO
Also, it doesn't make sense to want an empty string instead of NULL. Your ASP code can deal with NULL values just fine, otherwise you'd have to convert the resulting integers to strings every time. If you really want this you can say:
previousrec = COALESCE(CONVERT(VARCHAR(12), MIN(nA.galleryID)), ''),
nextrec = COALESCE(CONVERT(VARCHAR(12), MAX(nB.galleryID)), '')
But this will no longer work well when you move from ASP to ASP.NET because types are much more explicit. Much better to just have the application code be able to deal with, instead of being afraid of, NULL values.
This seems like a lot of work to get the previous and next ID, without retrieving any information about the current ID. Are you implementing paging? If so I highly recommend reviewing this article and this follow-up conversation.
Try this (nb not tested)
SELECT TOP 1 max(p.galleryID) as previousrec, min(n.galleryID) AS nextrec
FROM gallery AS p
CROSS JOIN gallery AS n
where (p.galleryid < #intID or p.galleryid is null)
and (n.galleryid > #intID or n.galleryid is null)
I'm assuming you validate that intID is an integer before using this code.
As for a stored procedure -- are you asking how to write a stored procedure? If so there are many tutorials which are quite good on the web.
Since Hogan contributed with the SQL statement, let me contribute with the stored proc part:
CREATE PROCEDURE spGetNextAndPreviousRecords
-- Add the parameters for the stored procedure here
#intID int
AS
BEGIN
-- SET NOCOUNT ON added to prevent extra result sets from
-- interfering with SELECT statements.
SET NOCOUNT ON;
SELECT TOP 1 max(p.galleryID) as previousrec, min(n.galleryID) AS nextrec
FROM gallery AS p
CROSS JOIN gallery AS n
where (p.galleryid < #intID or p.galleryid is null)
and (n.galleryid > #intID or n.galleryid is null)
END
And you call this from code as follows (assuming VB.NET):
Using c As New SqlConnection(ConfigurationManager.ConnectionStrings("ConnectionString").ConnectionString)
c.Open()
Dim command = New SqlCommand("spGetNextAndPreviousRecords")
command.Parameters.AddWithValue("#intID", yourID)
Dim reader as SqlDataReader = command.ExecuteReader()
While(reader.Read())
' read the result here
End While
End Using