How to check 2 expressions inside a decode function in PL SQL - plsql

Is it possible to check multiple Expressions inside "Decode" function in PL SQL.
My requirement is basically
SELECT value1,
DECODE(Expression1 AND Expression2,
search1 , search2,
,'pass result',
'fail result') value2
FROM dual;
Or is there are workaround for this?

The simplest answer is don't use decode, use a case statement instead, e.g.
select 1 value1,
case when 'a' = 'a' and 'b' = 'b' then 'pass'
else 'fail'
end result
from dual;
VALUE1 RESULT
---------- ------
1 pass
It is possible to use DECODE, but you'd have to use nested DECODEs and you'd end up with something that's much harder to read, understand and therefore maintain. And what happens if you need to add a third condition? Or maybe there are multiple sets of results that would result in a pass? You'd end up with something truly horrendous to try and understand!
But, if you really must, here's an example (along with the corresponding CASE expression solution, which is (IMHO) much easier to read):
with sample_data AS (SELECT 1 ID, 'a' col1, 'b' col2 FROM dual UNION ALL
SELECT 2 ID, 'a' col1, 'c' col2 FROM dual UNION ALL
SELECT 3 ID, 'd' col1, 'b' col2 FROM dual UNION ALL
SELECT 4 ID, 'e' col1, 'f' col2 FROM dual)
SELECT ID,
CASE WHEN col1 = 'a' AND col2 = 'b' THEN 'pass'
ELSE 'fail'
END case_result,
DECODE(col1, 'a', DECODE(col2, 'b', 'pass',
'fail'),
'fail') decode_result
FROM sample_data;
ID CASE_RESULT DECODE_RESULT
---------- ----------- -------------
1 pass pass
2 fail fail
3 fail fail
4 fail fail

Related

check for null values in all columns of a table using SQLite

I have a table with more than 15 columns. 2 of of them are of the type varchar, and most of them of type int and float.
I am new to SQL and am trying to figure out a way by which I can check if any of the columns have a NULL value in it.
Had there been just 4 or 5 columns I could have checked them individually with
SELECT COUNT(*) FROM table_name WHERE col1 IS NULL OR col2 IS NULL OR col3 IS NULL ...
But is there any efficient way to do this on a lot of columns in SQLite specifically?
I have referred to other questions regarding this here but I cannot use xml or store anything. Also I am using SQLite and can only run a query.
There is no way (that I know of) to check all columns if they contain null without explicitly listing all the column names.
Your query is the proper way to do it.
If you want to shorten (not significantly) the code you could use these alternatives:
SELECT COUNT(*) FROM table_name WHERE col1 + col2 + col3 IS NULL;
or:
SELECT COUNT(*) FROM table_name WHERE col1 || col2 || col3 IS NULL;
or:
SELECT COUNT(*) FROM table_name WHERE MAX(col1, col2, col3) IS NULL;
The above queries work, for any data type of the columns, because if there is even only 1 column equal to null then addition, concatenation and the scalar function MAX() (and MIN()) all return null.
See the demo.

Always return all row, even when query has no result

I have a "tt_Results" table which contains exactly three row / ID like:
ID | ResultType
---------------
1 | first
1 | second
1 | third
My query looks like:
select t.resultType
from tt_Results
where ID = 1;
Normally, this query should return all the three row. My problem is if one or more row not exist I must union all the three "type".
select res.* from
(
select resultType, '1' as exists
from tt_Results
where ID = 1
union all
select 'first' resulttype, '0' as exists
from dual
where not exists (
select resulttype
from tt_Results
where resulttype = 'first' and ID = 1)
union all
select 'second' resulttype, '0' as exists
from dual
where not exists (
select resulttype
from tt_Results
where resulttype = 'second' and ID = 1)
union all
select 'third' resulttype, '0' as exists
from dual
where not exists (
select resulttype
from tt_Results
where resulttype = 'third' and ID = 1)
) res
The final query result looks good but it is very slow. Anyone have a better PL/SQL solution for it? Thanks for the answers!
Left join to your table from a table of all types, using a case to calculate exists based on whether a join was made or not:
select
type,
case when resultType is null then '0' else '1' end as exists
from (select 'first' type from dual union
select 'second' from dual union
select 'third' from dual) t
left join tt_Results on resultType = type
and ID = 1
Note that the condition ID = 1 is part of the join condition, not in a where clause.
I recommend creating a 3-row table for the types, then the query would become simply:
select
type,
case when resultType is null then '0' else '1' end as exists
from types
left join tt_Results on resultType = type
and ID = 1
You can do this by creating a subquery to hold the result types you're interested in, and then using a partitioned outer join, like so:
with tt_results as (select 1 id, 'first' resulttype from dual union all
select 1 id, 'second' resulttype from dual union all
select 1 id, 'third' resulttype from dual union all
select 2 id, 'second' resulttype from dual union all
select 2 id, 'third' resulttype from dual union all
select 3 id, 'first' resulttype from dual),
dummy as (select 1 position, 'first' resulttype from dual union all
select 2 position, 'second' resulttype from dual union all
select 3 position, 'third' resulttype from dual)
select res.id,
d.resulttype,
case when res.resulttype is not null then 1 else 0 end res_exists
from dummy d
left outer join tt_results res partition by (res.id) on d.resulttype = res.resulttype
order by res.id,
d.position;
ID RESULTTYPE RES_EXISTS
---------- ---------- ----------
1 first 1
1 second 1
1 third 1
2 first 0
2 second 1
2 third 1
3 first 1
3 second 0
3 third 0
Adrian Billington has produced an excellent article on partititioned outer joins if you want to learn more about them.
If your tt_results table could contain more resulttypes than you're interested in, you might want/need to add a predicate to only grab the rows from the dummy subquery, otherwise you could get ids where all 3 resulttypes don't exist (although that may be what you want to see).
ETA: This will work if you need to select multiple ids in one go.

Eliminate ID if Col2 = (1,2,3)

I am using teradata sql, and I have the following input
If col2 has (1,2,3) values then delete the Id from table, so my desired table is:
I tried all possible ways, but cannot get to eliminate the IDs? Any help or suggestion will help. thanks
DELETE FROM yourTable
WHERE Id IN ( SELECT Id FROM yourTable WHERE col2 IN (1,2,3) )
If you actually want to delete those IDs you better use #MudassirHasan's solution, but if you want to select the other IDs you can utilize Conditional Aggregation in a Group Max:
select *
from mytable
qualify
max(case when col2 (1,2,3) then 1 else 0 end) -- will return zero when those values don't exist
over (partition by id) = 0 -- for an ID

sort semicolon separated values per row in a column

I want to sort semicolon separated values per row in a column. Eg.
Input:
abc;pqr;def;mno
xyz;pqr;abc
abc
xyz;jkl
Output:
abc;def;mno;pqr
abc;pqr;xyz
abc
jkl;xyz
Can anyone help?
Perhaps something like this. Breaking it down:
First we need to break up the strings into their component tokens, and then reassemble them, using LISTAGG(), while ordering them alphabetically.
There are many ways to break up a symbol-separated string. Here I demonstrate the use of a hierarchical query. It requires that the input strings be uniquely distinguished from each other. Since the exact same semicolon-separated string may appear more than once, and since there is no info from the OP about any other unique column in the table, I create a unique identifier (using ROW_NUMBER()) in the most deeply nested subquery. Then I run the hierarchical query to break up the inputs and then reassemble them in the outermost SELECT.
with
test_data as (
select 'abc;pqr;def;mno' as str from dual union all
select 'xyz;pqr;abc' from dual union all
select 'abc' from dual union all
select 'xyz;jkl' from dual
)
-- End of test data (not part of the solution!)
-- SQL query begins BELOW THIS LINE.
select str,
listagg(token, ';') within group (order by token) as sorted_str
from (
select rn, str,
regexp_substr(str, '([^;]*)(;|$)', 1, level, null, 1) as token
from (
select str, row_number() over (order by null) as rn
from test_data
)
connect by level <= length(str) - length(replace(str, ';')) + 1
and prior rn = rn
and prior sys_guid() is not null
)
group by rn, str
;
STR SORTED_STR
--------------- ---------------
abc;pqr;def;mno abc;def;mno;pqr
xyz;pqr;abc abc;pqr;xyz
abc abc
xyz;jkl jkl;xyz
4 rows selected.

Negative match set of strings in regexp_substr

I want to match all characters in a string until I hit one of the following substrings:
'='
'+%'
'-%'
I had no problem negating a set of single characters (i.e. [^=+-]), and I can positively match the strings with
(=|\+%|-%)
but I can't seem to figure out the syntax for negating the set. Any suggestions?
Couldn't do it with REGEXP_SUBSTR alone but here is one option:
with temp as
(
select 'TEST1=TEST1' test1 from dual union all
select 'TEST2+%TEST2' test1 from dual union all
select 'TEST3+TEST3' test1 from dual union all
select 'TEST4-%TEST4' test1 from dual union all
select 'TEST5-TEST5' test1 from dual
)
SELECT test1,
CASE
WHEN REGEXP_INSTR(test1, '+%|=|-%') = 0 THEN test1
ELSE REGEXP_REPLACE(SUBSTR(test1, 1, REGEXP_INSTR(test1, '-%|+%|=')), '[-=+%]')
END
result
FROM temp

Resources