Terse syntax to return an empty result set from SQL server - sql

Is there a shorter way to write the following?
-- select empty_result
select t.col
from (select 1 as col) t
where 1 = 0 -- never match
The "original" question follows. This was was modified may times, explicitly in hopes of stopping Y responses as a result of showing a specific use-case; and [rightly] claimed to be a confusing mess.
The use-case is a TSQL query that returns an empty result set in some cases while a 'real' result set in another. In both cases the structure is expected to be the same.
if #foo = 'bar'
-- select real_result
else
-- select empty_result
The question here is then, specifically about creating an empty result set / derived table simply.
One way to do this is as follows. Is there a syntactically 'simpler' method?
-- select empty_result
select t.col
from (select 1 as col) t
where 1 = 0 -- never match
An alternative in this specific MINIMAL IF..ELSE.. CASE, it could be constructed as the following SQL. It is (XY) outside the question scope even though it would function here as the resulting schema is the same in the example above. While it may be a good option elsewhere, it requires a different TSQL flow-control structure. It will also not return the correct result sets if both cases result in a different schema - making it too specific in respect to the title scope.
-- XY alternative specific to MINIMAL CASE shown above
-- select real_result or empty_result with single query
select t.col
from real_data t
where #foo = 'bar'

Yes, there is a 'simpler' / shorter / more terse syntax to returning an empty result set in SQL Server that does not require first creating a derived table.
-- select empty_result
select top 0
1 as col
This is SQL Server specific syntax. There might be other similar forms found in other database implementations.
While not specifically about a shorter empty result set syntax, Ken White provided an approach which avoids duplicating schema if it's identical in both cases. The IF flow-control structure is preserved, as per the original question.
if #foo = 'bar'
-- select real_result
select t.col
from real_table t
else
-- select empty_result
select t.col
from real_table t
where 1 = 0

Related

SQL use intermediate results

I have a column with numbers (float) that I would like to categorize and store a category as integer and as label (string). For now assume that the category is simply defined by the FLOOR(x).
This works:
SELECT salary,
FLOOR(salary) AS category_integer,
CASE WHEN FLOOR(salary) = 0
THEN 'foo'
ELSE 'bar'
END AS category_label
FROM test01
but I was wondering if I could use the intermediate variable 'category_integer' defined in the beginning of my query in a later part, something like this:
SELECT salary,
FLOOR(salary) AS category_integer,
CASE WHEN category_integer = 0
THEN 'foo'
ELSE 'bar'
END AS category_label
FROM test01
but this is apparently not how SQL works. I've looked into Common table Expressions but got lost there. Is there a way to reuse intermediate variables in an SQL expression?
SQL Fiddle
I must have missed this but I couldn't find related questions so far.
You may resort to common table expressions - basically a query that produces a labelled result set you can refer to in subsequent queries.
Adapted to your example:
with cte as (
select salary
, floor(salary) as category_integer
from test01
)
SELECT salary
, category_integer
, CASE WHEN category_integer = 0
THEN 'foo'
ELSE 'bar'
END AS category_label
FROM cte
;
Consult the reference for more details: CTE / WITH in pgSQL 9.6.
See it at work in SQL fiddle.
There are pre- and post-selection operations. For example order by and group by are post-selection instructions, distinct for example filters out duplicate results during the selection proces itself and as such duplicate results do not even enter the result set to be ordered or grouped.
When you use AS, you are telling PostgreSQL to take the result and put it in a column named category_integer in the output. You are not actually making a variable here that's available during query execution, as the result is only available after the query executes. As such, you can only do this with subselects where you have the result available as a virtual table in itself, where category_integer is a column in a table rather than a variable.
SELECT category_integer,
CASE WHEN category_integer = 0
THEN 'foo'
ELSE 'bar'
END AS category_label
FROM (SELECT FLOOR(0) AS category_integer FROM test01) AS test02
https://www.postgresql.org/docs/current/static/queries-select-lists.html
https://www.postgresql.org/docs/current/static/queries-table-expressions.html#QUERIES-TABLE-ALIASES

Can SELECT expressions sometimes be evaluated for rows not matching WHERE clause?

I would like to know if it's possible for expressions that are part of the SELECT statement list to be evaluated for rows not matching the WHERE clause?
From the execution order documented here, it seems that the SELECT gets evaluated long after the WHERE, however I ran into a very weird problem with a real-life query similar to the query below.
To put you in context, in the example, the SomeOtherTable has a a_varchar column which always contains numerical values for the code 105, but may contain non-numerical values for other codes.
The query statement works:
SELECT an_id, an_integer FROM SomeTable
UNION ALL
SELECT an_id, CAST(a_varchar AS int)
FROM SomeOtherTable
WHERE code = 105
The following query complains about being unable to cast a_varchar to int:
SELECT 1
FROM (
SELECT an_id, an_integer FROM SomeTable
UNION ALL
SELECT an_id, CAST(a_varchar AS int)
FROM SomeOtherTable
WHERE code = 105
) i
INNER JOIN AnotherOne a
ON a.an_id = i.an_id
And finally, the following query works:
SELECT 1
FROM (
SELECT an_id, an_integer FROM SomeTable
UNION ALL
SELECT
an_id,
CASE code WHEN 105 THEN CAST(a_varchar AS int) ELSE NULL END
FROM SomeOtherTable
WHERE code = 105
) i
INNER JOIN AnotherOne a
ON a.an_id = i.an_id
Therefore, the only explanation I could find was that with the JOIN, the query gets optimized differently in a way that CAST(a_varchar AS int) gets executed even if code <> 105.
The queries are run against SQL SERVER 2008.
Absolutely.
The documentation that you reference has a section called Logical Processing Order of the SELECT statement. This is not the physical processing order. It explains how the query itself is interpreted. For instance, an alias defined in the select clause cannot be references in the where clause, because the where clause is logically processed first.
In fact, SQL Server has the ability to optimize queries by doing various data transformation operations when it reads the data. This is a nice performance benefit, because the data is in memory, locally, and the operations can simply be done in place. However, the following can fail with a run-time error:
select cast(a_varchar as int)
from table t
where a_varchar not like '%[^0-9]%';
The filter is applied after the attempt at conversion, in the real process flow. I happen to consider this a bug; presumably, the folks at Microsoft do not think so, because they have not bothered to fix this.
Two workarounds are available. The first is try_convert(), which does conversions and returns NULL for a failure instead of a run-time error. The second is the case statement:
select (case when a_varchar not like '%[^0-9]%' then cast(a_varchar as int) end)
from table t
where a_varchar not like '%[^0-9]%';

sql server rewrites my query incorrectly?

There is a dirty data in input.
We are trying to cleanup dataset and then make some calculations on cleared data.
declare #t table (str varchar(10))
insert into #t select '12345' union all select 'ABCDE' union all select '111aa'
;with prep as
(
select *, cast(substring(str, 1, 3) as int) as str_int
from #t
where isnumeric(substring(str, 1, 3)) = 1
)
select *
from prep
where 1=1
and case when str_int > 0 then 'Y' else 'N' end = 'Y'
--and str_int > 0
Last 2 lines are doing the same thing. First one works, but if you uncomment second one it will crash with Conversion failed when converting the varchar value 'ABC' to data type int.
Obviously, SQL Server is rewriting query mixing all the conditions together.
My guess it that it considers 'case' as a havy operation and performs it as a last step. That's why workaround with case works.
Is this behavior documented in any way? or is it a bug?
This is a known issue with SQL Server, and Microsoft does not consider it a bug although users do. The difference between the two queries is the execution path. One is doing the conversion before the filtering, the other after.
SQL Server reserves the right to re-order the processing. The documentation does specify the logical processing of clauses as:
FROM
ON
JOIN
WHERE
GROUP BY
WITH CUBE or WITH ROLLUP
HAVING
SELECT
DISTINCT
ORDER BY
TOP
With (presumably but not explicitly documented here) CTEs being logically processed first. What does logically processed mean? Well, it doesn't mean that run-time errors are caught. It really determines the scope of identifiers during the compile phase.
When SQL Server reads from a data source, it can add new variables in. This is a convenient time to do this, because everything is in memory. However, this might occur before the filtering, which is what is causing the error when it occurs.
The fix to this problem is to use a case statement. So, the following CTE will usually work:
with prep as (
select *, (case when isnumeric(substring(str, 1, 3)) = 1 and str not like '%.%'
then cast(substring(str, 1, 3) as int)
end) as str_int
from #t
where isnumeric(substring(str, 1, 3)) = 1
)
Looks weird. And I think Redmond thinks so too. SQL Server 2012 introduced try_convert() (see here) which returns NULL if the conversion fails.
It would also help if you could instruct SQL Server to materialize CTEs. That would also solve the problem in this case. You can vote on adding such an option to SQL Server here.

SQL Server Empty Result

I have a valid SQL select which returns an empty result, up and until a specific transaction has taken place in the environment.
Is there something available in SQL itself, that will allow me to return a 0 as opposed to an empty dataset? Similar to isNULL('', 0) functionality. Obviously I tried that and it didn't work.
PS. Sadly I don't have access to the database, or the environment, I have an agent installed that is executing these queries so I'm limited to solving this problem with just SQL.
FYI: Take any select and run it where the "condition" is not fulfilled (where LockCookie='777777777' for example.) If that condition is never met, the result is empty. But at some point the query will succeed based on a set of operations/tasks that happen. But I would like to return 0, up until that event has occurred.
You can store your result in a temp table and check ##rowcount.
select ID
into #T
from YourTable
where SomeColumn = #SomeValue
if ##rowcount = 0
select 0 as ID
else
select ID
from #T
drop table #T
If you want this as one query with no temp table you can wrap your query in an outer apply against a dummy table with only one row.
select isnull(T.ID, D.ID) as ID
from (values(0)) as D(ID)
outer apply
(
select ID
from YourTable
where SomeColumn = #SomeValue
) as T
alternet way is from code, you can check count of DataSet.
DsData.Tables[0].Rows.count > 0
make sure that your query matches your conditions

How to select an empty result set?

Want to improve this post? Provide detailed answers to this question, including citations and an explanation of why your answer is correct. Answers without enough detail may be edited or deleted.
I'm using a stored procedure in MySQL, with a CASE statement.
In the ELSE clause of the CASE ( equivalent to default: ) I want to select and return an empty result set, thus avoiding to throw an SQL error by not handling the ELSE case, and instead return an empty result set as if a regular query would have returned no rows.
So far I've managed to do so using something like:
Select NULL From users Where False
But I have to name an existing table, like 'users' in this example.
It works, but I would prefer a way that doesn't break if eventually the table name used is renamed or dropped.
I've tried Select NULL Where False but it doesn't work.
Using Select NULL does not return an empty set, but one row with a column named NULL and with a NULL value.
There's a dummy-table in MySQL called 'dual', which you should be able to use.
select
1
from
dual
where
false
This will always give you an empty result.
This should work on most DBs, tested on Postgres and Netezza:
SELECT NULL LIMIT 0;
T-SQL (MSSQL):
SELECT Top 0 1;
How about
SELECT * FROM (SELECT 1) AS TBL WHERE 2=3
Checked in myphp, and it also works in sqlite and probably in any other db engine.
This will probably work across all databases.
SELECT * FROM (SELECT NULL AS col0) AS inner0 WHERE col0 IS NOT NULL;
SELECT TOP 0 * FROM [dbo].[TableName]
This is a reasonable approach to constant scan operator.
SELECT NULL WHERE FALSE;
it works in postgresql ,mysql, subquery in mysql.
How about this?
SELECT 'MyName' AS EmptyColumn
FROM dual
WHERE 'Me' = 'Funny'
SELECT * FROM (SELECT NULL) WHERE 0
In PostgreSQL a simple
SELECT;
works. You won't even get any columns labeled 'unknown'.
Note however, it still says 1 row retrieved.