How to get "short circuit" in sql query? - sql

I am meeting a database query demand like this:
If there is a record in which the column of 'type' is '1', then return the record.
select * from table where type = 1;
# if the result is not empty, then return it
Otherwise,
return the record in which the 'type' is '2'
select * from table where type = 2;
# even the result is empty
So, how can I merge the two queries into only one, like short circuit:
select * from table where type = 1 (short-circuit-or) type = 2;
# when 'type = 1' matches records, it will not execute 'type = 2'; otherwise, it does like 'union' action.
I will explain why I ask such question. Indeed, the first 3 answers are correct but not 'good' enough. I expect the time consumption is good as query of 'type = 1'. But union and in (1, 2) will cause the DBMS to query type = 2, which will slow the speed even type = 1 matches record.

In Oracle you can try:
SELECT *
FROM table
WHERE type IN (1, 2)
ORDER BY type
FETCH FIRST 1 ROWS ONLY;
In MySQL:
SELECT *
FROM table
WHERE type IN (1, 2)
ORDER BY type
LIMIT 1;
The idea is to get the rows, order them by some criteria and get the first one only.

hope this helps:
SELECT * FROM table WHERE type = 1
UNION
SELECT * FROM table WHERE type = 2 AND NOT EXISTS (SELECT * FROM table WHERE type = 1);
This will return all rows where type=1 and then append all rows where type=2 if and only if there are no rows where type=1.
Edited : I think this approach may be faster
SELECT *
FROM table
WHERE type IN (1, 2)
ORDER BY CASE WHEN type = 1 THEN 0 ELSE 1 END;
This query will return the first record that matches either type = 1 or type = 2, but it will prioritize records where type = 1 by ordering them before records where type = 2.

You can use the SQL UNION operator to combine the results of two SELECT statements into a single result set. Here is an example query that meets your requirement:
SELECT * FROM table WHERE type = 1
UNION ALL
SELECT * FROM table WHERE type = 2 AND NOT EXISTS (SELECT * FROM table WHERE type = 1);
This query first selects all records with type = 1. If this query returns any rows, they are returned as the result.
If the first query returns no rows, the second query is executed. This second query selects all records with type = 2 that do not have a corresponding row with type = 1. The NOT EXISTS clause ensures that only records with type = 2 that were not returned by the first query are included in the result.
By using UNION ALL, duplicates are not removed from the result set, which means that if a record has type = 1 and type = 2, it will be returned twice in the result set. If you want to remove duplicates, you can use just UNION instead of UNION ALL.

Related

SQL Server - do returned row values all exist in a set

I'm working on a stored procedure where I have a select query that can return multiple values. I'm looking for a way to see if those values exist in a different set of static values declared in the procedure.
So far it looks something like this:
DECLARE
#MachId1 = 1,
#MachId2 = 2,
#MachId3 = 3
SELECT DISTINCT pr.Machine from POs as rpo, Records as pr WHERE BoxId = #StoredProcParam and pr.PONO = rpo.poNo
-- I need to see if all the rows returned from the select query exist as a subset of {1, 2, 3}
Not entirely sure how to do this yet. I'm looking at using EXISTS but don't know how to define the set {1,2,3}. Maybe as a table, but how will this affect the performance of the stored proc?
SQL Server 2008
You can use EXCEPT to set minus the values from the result. If that results in an empty set the result must have been a (possible empty) subset of the values. If not, then the remaining element is not a member of the set of values. To check for the empty set you can use NOT EXISTS.
IF NOT EXISTS (SELECT DISTINCT
pr.machine
FROM pos rpo
INNER JOIN records pr
ON pr.pono = rpo.pono
WHERE boxid = #storedprocparam
EXCEPT
SELECT machid
FROM (SELECT #MachId1 machid
UNION
SELECT #MachId2 machid
UNION
SELECT #MachId3 machid) x)
BEGIN
PRINT 'Yes';
END
ELSE
BEGIN
PRINT 'No';
END;
You can use the WHERE IN clause.
SELECT DISTINCT pr.Machine from POs as rpo, Records as pr WHERE BoxId in (1,2,3) and pr.PONO = rpo.poNo
These values can also come from another table.
SELECT firstname FROM person where id in (select person_id from company where name = ‘Google’)

SQL Query : should return Single Record if Search Condition met, otherwise return Multiple Records

I have table with Billions of Records, Table structure is like :
ID NUMBER PRIMARY KEY,
MY_SEARCH_COLUMN NUMBER,
MY_SEARCH_COLUMN will have Numeric value upto 15 Digit in length.
What I want is, if any specific record is matched, I will have to get that matched value only,
i.e. : If I enter WHERE MY_SEARCH_COLUMN = 123454321 and table has value 123454321 then this only should be returned.
But if exact value is not matched, I will have to get next 10 values from the table.
i.e. : if I enter WHERE MY_SEARCH_COLUMN = 123454321 and column does not have the value 123454321 then it should return 10 values from the table which is greater than 123454321
Both the case should be covered in single SQL Query, and I have have to keep in mind the Performance of the Query. I have already created Index on the MY_SEARCH_COLUMN columns, so other suggestions are welcome to improve the Performance.
This could be tricky to do without using a proc or maybe some dynamic SQL, but we can try using ROW_NUMBER here:
WITH cte AS (
SELECT ID, MY_SEARCH_COLUMN,
ROW_NUMBER() OVER (ORDER BY MY_SEARCH_COLUMN) rn
FROM yourTable
WHERE MY_SEARCH_COLUMN >= 123454321
)
SELECT *
FROM cte
WHERE rn <= CASE WHEN EXISTS (SELECT 1 FROM yourTable WHERE MY_SEARCH_COLUMN = 123454321)
THEN 1
ELSE 10 END;
The basic idea of the above query is that we assign a row number to all records matching the target or greater. Then, we query using either a row number of 1, in case of an exact match, or all row numbers up to 10 in case of no match.
SELECT *
FROM your_table AS src
WHERE src.MY_SEARCH_COLUMN = CASE WHEN EXISTS (SELECT 1 FROM your_table AS src2 WITH(NOLOCK) WHERE src2.MY_SEARCH_COLUMN = 123456321)
THEN 123456321
ELSE src.MY_SEARCH_COLUMN
END

Display default value if query results in no records in BigQuery

A query can return an empty table on BigQuery. An example of such occurrence is if I join a bunch of tables in a query on BigQuery and the result of the joins is an empty table, or if there are no matches based on the where clause.
Here is a dumb sample query that will always return in an empty join:
#standardSQL
WITH query1 AS (
SELECT 1 AS number, "one" AS message
), query2 AS (
SELECT 2 AS number, "two" AS message)
SELECT "query result" AS result, query1.*
FROM query1
JOIN query2 ON query1.number = query2.number;
The query will show this output: Query returned zero records.
If that's the case I want to return either a message or a default row. But I don't know how to do that. I've tried using IFNULL, but that's only usuable for one column, not number of columns. Using an IF statement gave me errors as you can't return row(s) from an if statement.
I think the error it gave me was Scalar subquery cannot have more than one column unless using SELECT AS STRUCT to build STRUCT values.
Another thing that I could think of, but don't know how to implement is to add a UNION at the end that would only trigger if the previous parts didn't return anything. Or wrap the existing query in a WITH statement subquery and if that returns nothing, print a message, else do SELECT * FROM sub_query.
I'd like to either just display a message when an empty table is the result, or return a row with some default values.
I understand the answer is likely to contain a UNION statement and hence displaying just a message won't be possible. In that case I'd like to display a default row instead. For the above sample query a default row would look like: "No results found", NULL, NULL.
When the query returns a non empty table, I want it to look exactly like it did with the original query. So there shouldn't be any added columns or change to the schema of the result.
You would use union all. Something like this:
with t as (
. . . <all your query stuff here>
)
select cast(NULL as string) as msg, t.*
from t
union all
select msg, t.* -- all the `t` columns will be `NULL`
from (select 'No rows returned' as msg) left join
t
on 1 = 0 -- always false
where not exists (select 1 from t);
Note the complications. A query returns a fixed set of columns with a fixed set of names. This version returns an extra column at the beginning of the data to contain the message. In order to get all the rest of the columns, a left join is used, but the on clause is always false.
Option 1
Below displays row with all nulls in case if there is no result returned for your_query
#standardSQL
WITH your_query AS ( ... )
SELECT * FROM your_query
UNION ALL
SELECT your_query.* REPLACE ("No results found" AS result)
FROM (SELECT 1)
LEFT JOIN your_query ON FALSE
WHERE NOT EXISTS (SELECT 1 FROM your_query)
Row result number message
1 No results found null null
Option 2
If you know in advance output schema - below returns default row (assuming 0 default for number and "none" default for message
#standardSQL
WITH your_query AS ( ... )
SELECT * FROM your_query
UNION ALL
SELECT "No results found", 0, "none" FROM (SELECT 1)
LEFT JOIN your_query ON FALSE
WHERE NOT EXISTS (SELECT 1 FROM your_query)
Row result number message
1 No results found 0 none

teradata case when issue

I have the following queries which are supposed to give the same result, but drastically different
1.
select count(*)
from qigq_sess_parse_2
where str_vendor = 'natural search' and str_category is null and destntn_url = 'http://XXXX.com';
create table qigq_test1 as
(
select case
when (str_vendor = 'natural search' and str_category is null and destntn_url = 'http://XXXX.com' ) then 1
else 0
end as m
from qigq_sess_parse_2
) with data;
select count(*) from qigq_test1 where m = 1;
the first block gives a total number of count 132868, while the second one only gives 1.
What are the subtle parts in the query that causes this difference?
Thanks
When you create a table in Teradata, you can specify it to be SET or MULTISET. If you don't specify, it defaults to SET. A set table cannot contain duplicates. So at most, your new table will contain two rows, a 0 and a 1, since that's all that can come from your case statement.
EDIT:
After a bit more digging, the defaults aren't quite that simple. But in any case, I suspect that if you add the MULTISET option to your create statement, you'll see the behavior your expect.
My guess would be that your Create Table statement is only pulling in one row of data that fits the parameters for the following Count statement. Try this instead:
CREATE TABLE qigq_test1 (m integer);
INSERT INTO qigq_test1
SELECT
CASE
WHEN (str_vendor = 'natural search' and str_category IS NULL AND destntn_url = 'http://XXXX.com' ) THEN 1
ELSE 0
END AS m
FROM qigq_sess_parse_2;
SELECT COUNT(*) FROM qigq_test1 WHERE m = 1;
This should pull ALL ROWS of data from qigq_sess_parse_2 into qigq_test1 as either a 0 or 1.

AS ____ not working in "Union select statement"

I have this:
SELECT
COUNT(resurs_id) AS 'antalLikes'
FROM kopplaResursLikesUsers
WHERE resurs_id = 19
UNION
SELECT user_id AS 'gillarJagEller'
FROM kopplaResursLikesUsers
WHERE user_id = 5
And I thought the output would be:
antalLikes = 2
gillarJagEller = 5
but the result I get is:
antalLikes[0] = 2
antalLikes[1] = 5
Any ideas why it completely ignores my 2nd "AS" statement?
UNION does make an UNION for COLUMS, not for rows
So, it's taking the first select, get the column 'antalLikes'
and then make the union by the ordinal order.
So, it put the values from your 'gillarJagEller' column in the first one
to get your desired result, I suggest this:
select
(
SELECT
COUNT(resurs_id)
FROM kopplaResursLikesUsers
WHERE resurs_id = 19) AS 'antalLikes',
(
SELECT user_id
FROM kopplaResursLikesUsers
WHERE user_id = 5) AS 'gillarJagEller'
UNION takes as column names only the names specified in the first select statement, in this case, "antalLikes".
To get this:
antalLikes = 2 gillarJagEller = 5
You need something like this:
SELECT
COUNT(resurs_id) AS 'antalLikes',
(SELECT top 1 user_id
FROM kopplaResursLikesUsers
WHERE user_id = 5) AS 'gillarJagEller'
FROM kopplaResursLikesUsers
WHERE resurs_id = 19
Column names and column data types in a UNION are provided by the first select statement in the UNION. Any column names in subsequent `select statements are ignored.
Subsequent select statements in the UNION are required to have the same number of columns as the first select statement. Further, each column in each subsequent select statement must have a datatype that is identical to the column with the same ordinal number in the first select statement or is implicitly convertible to that data type.
WRT to nullity, I believe UNION sets the nullity of each column in the final result set by examining the nullity of that same column in each component select statement. Only if that column is non-nullable across each component select statement is the column in the final result set non-nullable.