Always have SQL query return atleast one record - sql

SELECT column_a,
CASE WHEN column_a > 10 THEN 0 ELSE 1 END AS column_b
FROM table_a;
This does not always returns record, my requirement is to always have at least 1 row returned.
Following a solution here I tried couple of workaround such as union but none of it works.
SELECT 2 as column_a,
CASE WHEN column_a > 10 THEN 0 ELSE 1 END AS column_b
FROM table_a
UNION
SELECT -1,-1 FROM table_a;
Is there a way I can make this query return a record even when none is found?

Well, query won't return anything if TABLE_A is empty (as there's no WHERE clause). In any other case, it'll return something.
Though, perhaps you meant to union it with a "dummy" select statement that selects from DUAL, not table_a (see line #6). Line #4 is here just to make the first select return no rows.
SQL> WITH table_a (column_a) AS (SELECT 3 FROM DUAL)
2 SELECT 2 AS column_a, CASE WHEN column_a > 10 THEN 0 ELSE 1 END AS column_b
3 FROM table_a
4 WHERE 1 = 2
5 UNION
6 SELECT -1, -1 FROM DUAL; --> DUAL here, not TABLE_A
COLUMN_A COLUMN_B
---------- ----------
-1 -1
SQL>

What is the problem you're trying to solve? Your title says "PL/SQL" which implies that this is happening in a PL/SQL block. In which case the standard answer would be just to catch the no_data_found exception and populate whatever variables you're fetching the data into with some appropriate default values.
BEGIN
SELECT column_a,
CASE WHEN column_a > 10 THEN 0 ELSE 1 END AS column_b
INTO local_variable1, local_variable2
FROM table_a;
EXCEPTION
WHEN no_data_found
THEN
local_variable1 := 1;
local_variable2 := -1;
END;
Likely, it would make sense to encapsulate this in a stored function.
If you really want to ensure that the query returns 1 row if there is no data in table_a, you could do something like this
SELECT column_a,
CASE WHEN column_a > 10 THEN 0 ELSE 1 END AS column_b
FROM table_a
UNION ALL
SELECT 1, -1
FROM dual
WHERE NOT EXISTS( SELECT 1 FROM table_a );

You can use:
SELECT *
FROM (
SELECT column_a,
CASE WHEN column_a > 10 THEN 0 ELSE 1 END AS column_b
FROM table_a
UNION ALL -- Use UNION ALL to keep duplicates from table_a.
SELECT -1,-1 FROM DUAL -- Use the DUAL table which always has exactly 1 row.
)
WHERE column_b >= 0 -- All the rows from table_a, if any exist.
OR ROWNUM = 1 -- Or, just the first row.
;
Which, for the empty table:
CREATE TABLE table_a (column_a INT);
Outputs:
COLUMN_A
COLUMN_B
-1
-1
And if you insert data:
INSERT INTO table_a (column_a)
SELECT 1 FROM DUAL UNION ALL
SELECT 2 FROM DUAL UNION ALL
SELECT 6 FROM DUAL UNION ALL
SELECT 10 FROM DUAL UNION ALL
SELECT 11 FROM DUAL UNION ALL
SELECT 11 FROM DUAL UNION ALL
SELECT 15 FROM DUAL;
Then the query outputs:
COLUMN_A
COLUMN_B
1
1
2
1
6
1
10
1
11
0
11
0
15
0
db<>fiddle here

Related

Compare before column in before row with next column in next row

My code is :
with x as
(
select 1 col from dual union all
select 2 col from dual union all
select 8 col from dual union all
select 4 col from dual union all
select 3 col from dual union all
select 2 col from dual
)
select col col1, col col2, col col3, rownum
from x
where col2.ROWNUM > col1.ROWNUM -1
and col2.ROWNUM > col3ROWNUM +1 ;
I want to compare col2.ROWNUM > col1.ROWNUM -1 and col2.ROWNUM > col3ROWNUM + 1 but that doesn't work and I got an error
ORA-01747: invalid user.table.column, table.column, or column specification
01747. 00000 - "invalid user.table.column, table.column, or column specification"
*Cause:
*Action:
Error at Line: 10 Column: 13
Please help me
It looks you got something wrong.
Result of that CTE is a single-column table whose only column is named col. There are no other columns.
SQL> with x as (
2 select 1 col from dual union all --> in UNION, all columns are
3 select 2 col from dual union all named by column name(s) from the
4 select 8 col from dual union all first SELECT statement
5 select 4 col from dual union all
6 select 3 col from dual union all
7 select 2 col from dual)
8 select x.*, rownum
9 from x;
COL ROWNUM
---------- ----------
1 1
2 2
8 3
4 4
3 5
2 6
6 rows selected.
SQL>
Therefore, where clause you wrote doesn't make any sense. Perhaps you should explain what you really have, rules that should be applied to source data and result you'd like to get.
Based on text you put into the title:
compare before column in before row with next column in next row
maybe you'd be interested in lag and lead analytic functions which then let you compare values in adjacent rows (pay attention to NULL values; I didn't). For example:
SQL> with x as (
2 select 1 col from dual union all
3 select 2 col from dual union all
4 select 8 col from dual union all
5 select 4 col from dual union all
6 select 3 col from dual union all
7 select 2 col from dual
8 ),
9 temp as
10 (select col,
11 rownum as rn
12 from x
13 ),
14 temp2 as
15 (select
16 rn,
17 col as this_row,
18 lag(col) over (order by rn) as previous_row,
19 lead(col) over (order by rn) as next_row
20 from temp
21 )
22 select this_row,
23 previous_row,
24 next_row,
25 --
26 case when this_row < previous_row then 'This < previous'
27 when this_row < next_row then 'This < next'
28 else 'something else'
29 end as result
30 from temp2
31 order by rn;
Result:
THIS_ROW PREVIOUS_ROW NEXT_ROW RESULT
---------- ------------ ---------- ---------------
1 2 This < next
2 1 8 This < next
8 2 4 something else
4 8 3 This < previous
3 4 2 This < previous
2 3 This < previous
6 rows selected.
SQL>
Use lead or lag functions. But, please, do not use rownum for such purposes.
Rownum indicates simply the order in which a row was found in the database and cannot be used for other purposes except limiting the number of rows fetched, like where rownum<=1 to be certain you won't get a too_many_rows exception, for instance. Still, if in a query you do fetch the pseud-column rownum, give it an alias so that you may use that value later on.
Moreover, what is supposed to mean col2.ROWNUM or col1.ROWNUM? That is not clear. col1 and col2 are two columns, which do not have the attribute rownum.
Something that may help in the future for analytic queries:
https://oracle-base.com/articles/misc/lag-lead-analytic-functions
And, if you wish to get a working SQL, please explain clearly what you wish to achieve, for I haven't really understood what that code is intended to do.
A way you may use rownum without getting errors:
with x as (
select 1 col from dual union all
select 2 col from dual union all
select 8 col from dual union all
select 4 col from dual union all
select 3 col from dual union all
select 2 col from dual)
,x2 as (
select col col1 ,col col2, col col3 ,rownum rn
from x
)
select *
from x2
where rn between 2 and 3 --- rownum cannot be used in such a
condition!!!
;
Or, to be certain you get only the first row from a table satisfying a given condition:
select x_col1, x_col2 into v_col1, v_col2
from x_table
where ... --- logical conditions
and rownum<=1; --- rownum <= 1 avoids too_many_rows_exception if several rows satisfy the logical conditions given before
In Oracle, results sets have a non-deterministic order (i.e. they are unordered) unless you use an ORDER BY clause. Therefore, if you have a physical table, you need another column to provide the order (rather than relying on the ROWNUM pseudo-column, which may result in unexpected behaviour):
CREATE TABLE x (order_id, col) AS
SELECT 1, 1 FROM DUAL UNION ALL
SELECT 2, 2 FROM DUAL UNION ALL
SELECT 3, 8 FROM DUAL UNION ALL
SELECT 4, 4 FROM DUAL UNION ALL
SELECT 5, 3 FROM DUAL UNION ALL
SELECT 6, 2 FROM DUAL;
If you want to find the rows that go up in succession, then you can use MATCH_RECOGNIZE for row-by-row pattern matching:
SELECT *
FROM x
MATCH_RECOGNIZE(
ORDER BY order_id
MEASURES
any_row.col AS col1,
FIRST(up.col) AS col2,
LAST(up.col) AS col3,
FIRST(order_id) AS start_order_id
PATTERN ( any_row up{2} )
DEFINE up AS ( col > PREV(col) )
)
or the LEAD analytic function:
SELECT *
FROM (
SELECT col AS col1,
LEAD(col, 1) OVER (ORDER BY order_id) AS col2,
LEAD(col, 2) OVER (ORDER BY order_id) AS col3,
order_id
FROM x
)
WHERE col2 > col1
AND col3 > col2;
Which both output:
COL1
COL2
COL3
START_ORDER_ID
1
2
8
1
fiddle
It looks like you want to find the rows where the value of the column is bigger than it is in both - the previous and next row. If so, you could try this:
WITH
tbl (ID, COL) AS -- Sample data (ID column is just to preserve order of the rows)
(
Select 1, 1 From Dual Union All
Select 2, 2 From Dual Union All
Select 3, 8 From Dual Union All
Select 4, 4 From Dual Union All
Select 5, 3 From Dual Union All
Select 6, 2 From DUAL
)
Select ID, COL, CASE WHEN COL > LAG(COL, 1) OVER(Order By ID) And COL > LEAD(COL, 1) OVER(Order By ID) THEN 'YES' END "BIGGER_THAN_PREV_AND_NEXT"
From tbl
Order By ID
ID COL BIGGER_THAN_PREV_AND_NEXT
---------- ---------- -------------------------
1 1
2 2
3 8 YES
4 4
5 3
6 2
... with a bit different sample data this will find the other row(s) that satisfy the condition ...
WITH
tbl (ID, COL) AS -- Sample data (ID column is just to preserve order of the rows)
(
Select 1, 1 From Dual Union All
Select 2, 2 From Dual Union All
Select 3, 8 From Dual Union All
Select 4, 4 From Dual Union All
Select 5, 5 From Dual Union All -- value of COL changed from 3 to 5
Select 6, 2 From DUAL
)
Select ID, COL, CASE WHEN COL > LAG(COL, 1) OVER(Order By ID) And COL > LEAD(COL, 1) OVER(Order By ID) THEN 'YES' END "BIGGER_THAN_PREV_AND_NEXT"
From tbl
Order By ID
ID COL BIGGER_THAN_PREV_AND_NEXT
---------- ---------- -------------------------
1 1
2 2
3 8 YES
4 4
5 5 YES
6 2
OR without ID - using ROWNUM (as in your question), - not adviseable, though...
WITH
tbl (COL) AS -- Sample data (without ID column)
(
Select 1 From Dual Union All
Select 2 From Dual Union All
Select 8 From Dual Union All
Select 4 From Dual Union All
Select 5 From Dual Union All
Select 2 From DUAL
)
Select COL, CASE WHEN COL > LAG(COL, 1) OVER(Order By ROWNUM) And COL > LEAD(COL, 1) OVER(Order By ROWNUM) THEN 'YES' END "BIGGER_THAN_PREV_AND_NEXT"
From tbl
COL BIGGER_THAN_PREV_AND_NEXT
---------- -------------------------
1
2
8 YES
4
5 YES
2
Any Order By clause added to the query could change the ROWNUM values and the result...

How can I get the distinct ids where ALL rows match a certain criteria in GBQ?

I'm querying a database to get the distinct ids where all the rows with each id match the criteria. For example, I would like to query the table below to get the distinct id where all values are truue. In this case, I would only return a single row with the id of 1.
Column A
Column B
1
true
1
true
2
false
2
true
2
true
3
false
3
false
3
false
Expected result
ColumnA
1
Currently, I have a query such as this
select
columnA
from
table
group by
columnA
having
(count(columnB = false) = 0)
But I end up returning no data at all. Not an error, just nothing matching my query. This is an example with dummy data, but the actual DB is quite large so I would expect lots of data back.
Any help is appreciated!
consider to use LOGICAL_AND function.
WITH sample_table AS (
SELECT 1 column_a, true column_b UNION ALL
SELECT 1 column_a, true column_b UNION ALL
SELECT 2 column_a, false column_b UNION ALL
SELECT 2 column_a, true column_b UNION ALL
SELECT 2 column_a, true column_b UNION ALL
SELECT 3 column_a, false column_b UNION ALL
SELECT 3 column_a, false column_b UNION ALL
SELECT 3 column_a, false column_b
)
SELECT column_a
FROM sample_table
GROUP BY 1
HAVING LOGICAL_AND(column_b) IS TRUE;
+----------+
| column_a |
+----------+
| 1 |
+----------+

sql query for like to Order exact match at top in list

sql query for like to show exact match first in list:
dataset: "abcd", "a","b","bc","bcd"
select * from table where data like "%bc%";
it should show in order bc, abcd, bcd.
As you said - sort by match.
SQL> with test (col) as
2 (select 'abcd' from dual union all
3 select 'a' from dual union all
4 select 'b' from dual union all
5 select 'bc' from dual union all
6 select 'bcd' from dual
7 )
8 select col
9 from test
10 where col like '%' || '&&par_search_string' ||'%'
11 order by utl_match.jaro_winkler_similarity(col, '&&par_search_string') desc;
Enter value for par_search_string: bc
COL
----
bc
bcd
abcd
SQL> undefine par_search_string
SQL> /
Enter value for par_search_string: cd
COL
----
bcd
abcd
SQL>
One of many methods:
with
t as (
select 'abcd' c from dual union all
select 'a' from dual union all
select 'b' from dual union all
select 'bc' from dual union all
select 'bcd' from dual
)
select *
from t
where c like '%bc%'
order by length(c)
Demo.
I think you can use a query like this one that can return your expected result.
select * from table where data='bc'
union all
select * from table where data like '%bc%' and data<>'bc'
One method is:
select *
from t
where data like '%bc%'
order by (case when data = 'bc' then 1 else 2 end);
Or if you don't want to type so much:
order by nullif(data, 'bc') desc
The descending sort puts NULL values first.

How to assign two columns with CASE in SQL query

How can i assign values for more than one column in my SQL query with CASE statement. When i tried as shown below i am getting error.
select case when 1=1 then 'Y' column1, 'Yes' column2 end from dual;
ORA-00905: missing keyword
00905. 00000 - "missing keyword"
*Cause:
*Action:
Error at Line: 24 Column: 31
Can you please help.
Perhaps you want:
select 'Y' as column1, 'Yes' as column1
from dual
where 1=1
union all
select NULL, NULL
from dual
where not (1=1);
Use aggregation and a WHERE clause:
select max('Y') column1, max('Yes') column2
from dual
where 1=1
Result:
> COLUMN1 | COLUMN2
> :------ | :------
> Y | Yes
Or:
select max('Y') column1, max('Yes') column2
from dual
where 1=2
Result:
> COLUMN1 | COLUMN2
> :------ | :------
> null | null
See the demo.
Repeat the case:
SQL> select case when 1 = 1 then 'Y'
2 else 'N'
3 end column1,
4 --
5 case when 1 = 1 then 'Y'
6 else 'N'
7 end column2
8 from dual;
C C
- -
Y Y
SQL>
If you don't want to repeat it, a CTE is one option you might use:
SQL> with temp as
2 (select case when 1 = 1 then 'Y'
3 else 'N'
4 end val
5 from dual
6 )
7 select val as column1,
8 val as column2
9 from temp;
C C
- -
Y Y
SQL>
Or, even less typing:
SQL> select val as column1,
2 val as column2
3 from (select decode(1, 1, 'Y', 'N') va
4 from dual);
C C
- -
Y Y
SQL>

Can i get the rows that isn't repeated inverted in my table?

I have a table:
1|5
2|4
3|3
4|2
5|1
(the actual table is a lot of names and what I want is to just get one set of each combination)
is there a way to get just the rows that are not repeated inverted at the end?
I just want the:
1|5
2|4
3|3
rows.. can i do this in sql?
Something like:
select distinct(case when x<y then x||'|'||y else y||'|'||x end) from table;
Should work on Oracle, in different db use equivalent of case.
Test:
select distinct(case when x<y then x||'|'||y else y||'|'||x end) from
(select 1 x,2 y from dual
union
select 1 x,2 y from dual
union
select 1 x,3 y from dual
union
select 3 x,2 y from dual
union
select 2 x,1 y from dual
);
Returns:
1|2
1|3
2|3
Would this solve your problem:
select *
from MyTable
where ColA <= ColB
Edit: Ok, if you have cases like 20, 5 then you could use this:
with allpairs as (
select ColA, ColB
from MyTable
where ColA <= ColB
union all
select ColB, ColA
from MyTable
where ColB < ColA
)
select distinct *
from allpairs