Using decode in where clause - sql

I have a DECODE in my WHERE clause like this:
Where id = decode('&flag','Yes',(Select id from tab where id > 10),0)
This code works if the subquery returns one post. If I returns several I get an error like, ORA-01427, "Single-row subquery returns more than one row"
I've tried to change the '=' to an 'in' but I still get the same error!
Any ideas?
extended example:
WHERE Dop_id = (DECODE ('&prep_flag', 'Yes',
(SELECT Dop_id FROM
( SELECT DOP_id, name FROM TABLE)
WHERE name IS NOT NULL)
, Dop_id))
as mention this works if the select statmen returns on row, and not several.

Assuming decode is only expecting a single value where you have Select id from tab where id > 10 I would try moving the select outside of decode:
WHERE id IN (
SELECT decode('&flag', 'Yes', id, 0)
FROM tab
WHERE id > 10
)

WHERE Dop_id IN
( CASE &flag
WHEN 'Yes'
THEN (SELECT Dop_id
FROM TABLE
WHERE name IS NOT NULL)
ELSE Dop_id);

Related

Why this sql will cause type conversion error?

WITH tb_testl AS (
SELECT 1 AS id ,'hehe' AS value
UNION ALL
SELECT 1 AS id, '1' AS value
UNION ALL
SELECT 2 AS id, '2' AS value
UNION ALL
SELECT 2 AS id, '2' AS value
), tb_test2 AS (
SELECT CONVERT(INT , value) AS value FROM tb_testl WHERE id = 2
)
SELECT * FROM tb_test2 WHERE value = 2;
this sql will cause error
Conversion failed when converting the varchar value 'hehe' to data
type int.
but the table tb_test2 dosen't have the value 'hehe' which is in the anthor table tb_test1. And I found that this sql will work well if I don't append the statement WHERE value = 2; .I've tried ISNUMBERIC function but it didn't work.
version:mssql2008 R2
With respect to the why this occurs:
There is a Logical Processing Order, which describes the order in which clauses are evaluated. The order is:
FROM
ON
JOIN
WHERE
GROUP BY
WITH CUBE or WITH ROLLUP
HAVING
SELECT
DISTINCT
ORDER BY
TOP
You can also see the processing order when you SET SHOWPLAN_ALL ON. For this query, the processing is as follows:
Constant scan - this is the FROM clause, which consists of hard coded values, hence the constants.
Filter - this is the WHERE clause. While it looks like there are two where clauses (WHERE id = 2 and WHERE value = 2). SQL Server sees this differently, it considers a single WHERE clause: WHERE CONVERT(INT , value) = 2 AND id = 2.
Compute scaler. This is the CONVERT function in the select.
Because both WHERE clauses are executed simultaneously, the hehe value is not filtered out of the CONVERT scope.
Effectively, the query is simplified to something like:
SELECT CONVERT(INT, tb_testl.value) AS Cvalue
FROM (
SELECT 1 AS id
, 'hehe' AS value
UNION ALL
SELECT 1 AS id
, '1' AS value
UNION ALL
SELECT 2 AS id
, '2' AS value
UNION ALL
SELECT 2 AS id
, '2' AS value
) tb_testl
WHERE CONVERT(INT, tb_testl.value) = 2
AND tb_testl.id = 2
Which should clarify why the error occurs.
With SQL, you cannot read code in the same way as imperative languages like C. Lines of SQL code are not necessarily (mostly not at all, in fact) executed in the same order it is written in. In this case, it's an error to think the inner where is executed before the outer where.
SQL Server does not guarantee the order of processing of statements (with one exception below). That is, there is no guarantee that WHERE filtering happens before the SELECT. Or that one CTE is evaluated before another. This is considered an advantage because it allows SQL Server to rearrange the processing to optimize performance (although I consider the issue that you are seeing a bug).
Obviously, the problem is in this part of the code:
tb_test2 AS (
SELECT CONVERT(INT, value) AS value
FROM tb_testl
WHERE id = 2
)
(Well, actually, it is where tb_test2 is referenced.)
What is happening is that SQL Server pushes the CONVERT() to where the values are being read, so the conversion is attempted before the WHERE clause is processed. Hence, the error.
In SQL Server 2012+, you can easily solve this using TRY_CNVERT():
tb_test2 AS (
SELECT TRY_CONVERT(INT, value) AS value
FROM tb_testl
WHERE id = 2
)
However, that doesn't work in SQL Server 2008. You can use the fact that CASE does have some guarantees on the order of processing:
tb_test2 AS (
SELECT (CASE WHEN value NOT LIKE '%[^0-9]%' THEN CONVERT(INT, value)
END) AS value
FROM tb_testl
WHERE id = 2
)
error caused by this part of statement
), tb_test2 AS (
SELECT CONVERT(INT , value) AS value FROM tb_testl WHERE id = 2
value has type of varchar and 'hehe' value cannot be converted to integer
WITH tb_testl AS (
SELECT 1 AS id ,'hehe' AS value
UPDATE: sql try convert all value(s) to integer in you statement. to avoid error rewrite statement as
WITH tb_testl AS (
SELECT 1 AS id ,'hehe' AS value
UNION ALL SELECT 1 AS id, '1' AS value
UNION ALL SELECT 2 AS id, '2' AS value
UNION ALL SELECT 2 AS id, '2' AS value
), tb_test2 AS (
SELECT value AS value FROM tb_testl WHERE id = 2
),
tb_test3 AS (
SELECT cast(value as int) AS value FROM tb_test2
)
SELECT * FROM tb_test3

SQL Select empty

I have the following code in Oracle 11:
select xmlelement("foo", xmlagg(xmlelement("bar",myValues))) from someTable where rownum = 0; --Changing the rownum from 0 should give me values
The output currently is: <foo></foo>
I would instead like this to return nothing, or null, when no rows are select. Otherwise it'll be a XMLtype with the aggregated data like how I have it above.
How would I be able to achieve this for the case when no rows are selected?
You can use case select:
select case when count (*) != 0 then xmlelement("foo", xmlagg(xmlelement("bar",myValues))) end
You can convert to CLOB using getClobval function and do a comparison.
SELECT
xml
FROM
(
SELECT
xmlelement("foo",xmlagg(xmlelement("bar",myvalues) ) ).getclobval() xml
FROM
sometable
)
WHERE
TO_CHAR(xml) != '<foo></foo>';

Returning a default value when query does not return rows in DB2

For a query I am using, a default value will need to be returned if no rows are returned (since the output will be used downstream). The problem I'm encountering is how to programmatically identify that zero or null rows are returned so that the query knows to use the 'default' value.
SELECT DISTINCT fieldName from DB2Table
WHERE qualifier1 = '___'
AND qualifier2 = '___';
This can return either a value or nothing (as in, no rows at all). I've attempted using count(*), NOT NULL, and EXISTS() within a CASE Statement, but I've had no luck.
**Psuedocode**:
IF query returns values, return those
ELSE return "Some Value"
Any tips/insights would be greatly appreciated!
If the query doesn't return a row and a default value needs to be returned in that case, use
SELECT
COALESCE(
(SELECT DISTINCT fieldName from DB2Table
WHERE qualifier1 = '___' AND qualifier2 = '___'), 'DEFAULTVALUE'
)
FROM sysibm.sysdummy1
If you are only expecting one row, then use aggregation:
SELECT COALESCE(fieldName, 'DEFAULT VALUE')
FROM DB2Table
WHERE qualifier1 = '___' AND qualifier2 = '___';
If you could be getting multiple rows, then here is another method:
WITH t as (
SELECT fieldName
FROM DB2Table
WHERE qualifier1 = '___' AND qualifier2 = '___'
)
SELECT t.*
FROM t
UNION ALL
SELECT 'DEFAULT VALUE'
FROM sysibm.sysdummy1
WHERE NOT EXISTS (SELECT 1 FROM t);

return a default record from a sql query

I have a sql query that I run against a sql server database eg.
SELECT * FROM MyTable WHERE Id = 2
This may return a number of records or may return none. If it returns none, I would like to alter my sql query to return a default record, is this possible and if so, how? If records are returned, the default record should not be returned. I cannot update the data so will need to alter the sql query for this.
Another way (you would get an empty initial rowset returned);
SELECT * FROM MyTable WHERE Id = 2
IF (##ROWCOUNT = 0)
SELECT ...
SELECT TOP 1 * FROM (
SELECT ID,1 as Flag FROM MyTable WHERE Id = 2
UNION ALL
SELECT 1,2
) qry
ORDER BY qry.Flag ASC
You can have a look to this post. It is similar to what you are asking
Return a value if no rows are found SQL
I hope that it can guide you to the correct path.
if not exists (SELECT top 1 * FROM mytable WHERE id = 2)
select * from mytable where id= 'whatever_the_default_id_is'
else
select * from mytable where id = 2
If you have to return whole rows of data (and not just a single column) and you have to create a single SQL query then do this:
Left join actual table to defaults single-row table
select
coalesce(a.col1, d.col1) as col1,
coalesce(a.col2, d.col2) as col2,
...
from (
-- your defaults record
select
default1 as col1,
default2 as col2,
...) as d
left join actual as a
on ((1 = 1) /* or any actual table "where" conditions */)
The query need to return the same number of fields, so you shouldn't do a SELECT * FROM but a SELECT value FROM if you want to return a default value.
With that in mind
SELECT value FROM MyTable WHERE Id = 2
UNION
SELECT CASE (SELECT count(*) FROM MyTable WHERE Id = 2)
WHEN 0 THEN 'defaultvalue'
END

select Case with subqueries select and using Top

Hi i want to retrive the list of browsers used by user id
Table Contains
UserID int
BrowserName nvarchar(40)
here is my Query
select browser =
CASE
WHEN ( PATINDEX('%IE%',BrowserName) IS not null) THEN SUBSTRING(BrowserName,PatIndex('%IE%',BrowserName),8)
WHEN (PATINDEX('%Firefox%',BrowserName) IS not null) THEN SUBSTRING(BrowserName,PatIndex('%Firefox%',BrowserName),8)
WHEN (PATINDEX('%Chrome%',BrowserName) IS not null) THEN SUBSTRING(BrowserName,PatIndex('%Chrome%',BrowserName),6)
END
from tableBrowsers where UserId =21
But how to select only top 1 substring in this query .
eg : after when in case statement i n need only one row returned for that case, i tried this, but not getting idea how to implement in case
THEN select top 1 SUBSTRING(BrowserName,PatIndex('%IE%',BrowserName),8) from browsertable
output will be like this
IE
FIREFOX
CHROME
if the user used three browsers
Just add DISTINCT:
SELECT DISTINCT browser =
CASE
WHEN ( PATINDEX('%IE%',BrowserName) IS not null) THEN SUBSTRING(BrowserName,PatIndex('%IE%',BrowserName),8)
WHEN (PATINDEX('%Firefox%',BrowserName) IS not null) THEN SUBSTRING(BrowserName,PatIndex('%Firefox%',BrowserName),8)
WHEN (PATINDEX('%Chrome%',BrowserName) IS not null) THEN SUBSTRING(BrowserName,PatIndex('%Chrome%',BrowserName),6)
END
FROM tableBrowsers where UserId =21