SQL: If no rows found, do another search? - sql

I need to write a query which would always return something, even if nothing satisfies conditions, something like this
SELECT * WHERE date > NOW() FROM table ORDER by date
IF none is returned THEN
SELECT FIRST FROM table ORDER by date
So only numbers more then 10 will be returned, if none is returned, return any number
Any ideas how to do it?

Here is one way, using union all:
select *
from table
where number > 10
union all
(select *
where number > 0 and
not exists (select * from table where number > 10)
limit 1
)
If you are using a reasonable version of SQL, you could do something like:
select t.*
from (select t.*, max(number) over () as maxnumber,
row_number() over (order by number desc) as seqnum
from table t
) t
where (maxnumber > 10 and number > 10) or seqnum = 1
You need window functions for this.

Related

SQL Offset return last row if no match

I want to return the book that matches the offset/order and if there isn't one I want to return the last book.
WITH BOOKS AS (
SELECT 'book_1' AS name, 0 AS order_ UNION ALL
SELECT 'book_2', 1 UNION ALL
SELECT 'book_3', 2
)
SELECT name from BOOKS ORDER BY order_ asc OFFSET 0 limit 1;
It should return:
Offset 0 - book_1
Offset 1 - book_2
Offset 2 - book_3
Offset 3+ - book_3
It works fine for offset 0,1,2 but not really sure how I can make it work for offset bigger than 3.
Fiddle
Thanks
If when you will create your own function which returns table data then you can do it easily using the if-exists (or you can use any logic) statements in the function. But doing this using one query is more difficult. For that, I choose the second way, so using a query.
My Sample Query:
with mytable as materialized
(
select
*,
row_number() over (order by order_) as r_num
from
test.test_table
)
select * from (
(SELECT * from mytable order by r_num offset 17 limit 1)
union
(SELECT * from mytable where r_num = (select max(r_num) from mytable))
) t1
limit 1;
This is my logic variant, maybe someone has another best variants.

Using subtraction of two queries in SELECT TOP query

I am struggling to prepare a query like this in SQL Server:
I have a table where I have a specific, constant value, let's say it's 15 (column defined as float)
In the same table I have one column where sometimes there is a value and sometimes it is a NULL value
So I would like to use SELECT TOP () query that would show me the number of records that is a result of subtraction of two queries:
SELECT
(SELECT DISTINCT Records
FROM Brand.Alle
WHERE HdNummer = '33')
-
(SELECT COUNT(AbrufNr)
FROM Brand.Alle
WHERE HdNummer = '33'
AND Transaction IS NOT NULL) AS DIFFERENCE
This query returns the result I want to have (let's say 13).
I would like to have selected top 13 records from a table I run a query against:
SELECT TOP (SELECT
(SELECT DISTINCT Records FROM Brand.Alle
WHERE HdNummer = '33')
-
(SELECT COUNT(AbrufNr) FROM Brand.Alle
WHERE HdNummer = '33' AND Transaction IS NOT NULL) AS DIFFERENCE) *
FROM Brand.Alle
WHERE HdNummer = '33' AND Transaction IS NULL
ORDER BY NEWID()
but it fails due to an error saying that I need to use an integer in select top statement. So the question is: how can I convert the value I receive as a result of subtraction two queries so I could use in in SELECT TOP?
I would highly appreciate any help.
Thank you in advance.
Number all your rows and only keep those with a number less or equal to the desired count.
with numbered as
(
select
alle.*,
row_number() over (order by newid()) as rn
from brand.alle
where hdnummer = 33
)
select *
from numbered
where rn <= ( <your count query here> );
Your count query can probably written shorter along the lines of:
select
count(distinct records) -
count(case when transaction is not null then abrufnr end) as cnt
from brand.alle
where hdnummer = 33;
And you can even combine the two with window functions in order to read from the table only once.
with numbered_and_counted as
(
select
alle.*,
count(distinct records) over () -
count(case when transaction is not null then abrufnr end) over () as cnt,
row_number() over (order by newid()) as rn
from brand.alle
where hdnummer = 33
)
select *
from numbered_and_counted
where rn <= cnt;

Exclude records where count > 5 and select top 1 of it

I want to exclude records where id > 5 then select the top 1 of it order by date. How can I achieve this? Each record has audit_line which is unique field for each record. Recent SQL script is on below:
SELECT *
FROM db.table
HAVING COUNT(id) > 5
If you want id > 5 then you want where:
select top (1) t.*
from db.table t
where id > 5
order by date;
You can use row-numbering for this.
Note that if you have no other column to order by, you can do ORDER BY (SELECT NULL), but then you may get different results on each run.
SELECT *
FROM (
SELECT *, ROW_NUMBER() OVER (PARTITION BY id ORDER BY some_other_column) rn
FROM table
) t
WHERE rn = 5;

SQL Query Pattern Selecting

Table part
I need to select all from the rows where the first 7 characters of the Assoc.Ref column are the same on a specific day.
Result example
You need aggregation :
SELECT t.col
FROM table t
GROUP BY t.col
HAVING COUNT(*) > 1;
If you want exactly two rows for each then use COUNT(*) = 2 instead .
If you want all rows then you can use windows function :
SELECT t.*
FROM (SELECT t.*,
COUNT(*) OVER(PARTITION BY col) AS cnt
FROM table t
) t
WHERE t.cnt > 1;
EDIT : After made update on question you might need LEFT() :
SELECT t.*
FROM (SELECT t.*,
COUNT(*) OVER(PARTITION BY CAST(Date_created AS date), LEFT(associated_ref, 7)) AS cnt
FROM table t
) t
WHERE t.cnt > 1 AND CAST(t.Date_created AS date) = '2019-02-08';
If the Date_created has no time then no conversation is needed. Just use Date_created instead.

Pagination of large dataset

I have a query that returns a large (10000+ rows) dataset. I want to order by date desc, and display the first 40 results. Is there a way to run a query like this that only retrieves those 40 results without retrieving all 10000 first?
I have something like this:
select rownum, date, * from table
order by date desc
This selects all the data and orders it by date, but the rownum is not in order so it is useless for selecting only the first 40.
ROW_NUMBER() over (ORDER BY date desc) AS rowNumber
^ Will display a rownumber in order, but I can't use it in a where clause because it is a window function. I could run this:
select * from (select ROW_NUMBER() over (ORDER BY date desc) AS rowNumber,
rownum, * from table
order by date desc) where rowNumber between pageStart and pageEnd
but this is selecting all 10000 rows. How can I do this efficiently?
SELECT *
FROM (SELECT *
FROM table
ORDER BY date DESC)
WHERE rownum <= 40
will return the first 40 rows ordered by date. If there is an index on date that can be used to find these rows, and assuming statistics are up to date, Oracle should choose to use that index to identify the 40 rows that you want and then do 40 single-row lookups against the table to retrieve the rest of the data. You could throw a /*+ first_rows(40) */ hint into the inner query if you want though that shouldn't have any effect.
For a more general discussion on pagination queries and Top N queries, here's a nice discussion from Tom Kyte and a much longer AskTom discussion.
Oracle 12c has introduced a row limiting clause:
SELECT *
FROM table
ORDER BY "date" DESC
FETCH FIRST 40 ROWS ONLY;
In earlier versions you can do:
SELECT *
FROM ( SELECT *
FROM table
ORDER BY "date" DESC )
WHERE ROWNUM <= 40;
or
SELECT *
FROM ( SELECT *,
ROW_NUMBER() OVER ( ORDER BY "date" DESC ) AS RN
FROM table )
WHERE RN <= 40;
or
SELECT *
FROM TEST
WHERE ROWID IN ( SELECT ROWID
FROM ( SELECT "Date" FROM TEST ORDER BY "Date" DESC )
WHERE ROWNUM <= 40 );
Whatever you do, the database will need to look through all the values in the date column to find the 40 first items.
You don't need a window function. See
http://www.techonthenet.com/oracle/questions/top_records.php
for an answer to your problem.