Oracle syntax for subselect in FROM clause? - sql

According to Using Subqueries Oracle SQL accepts a subquery in the from-clause of a select statement, like
SELECT * FROM ( SELECT a FROM b );
However, looking at the SELECT documentation, I see no possibility to get to select/subquery in the from clause (e.g. from the rules table_reference or join_clause).
Am I missing something here, is this part of the SQL grammar documented elsewhere? Or is this another incomplete part of the documentation?

In the documentation you linked, you can see that table_reference can be a query_table_expression which can be a ( subquery ).

Oracle syntax diagrams are pretty thorough:
The names you are looking for are:
table-reference --> query-table_expression
query-table-expression --> subquery
It is a bit confusing because of the optional lateral.
I do wonder if the lateral is allowed everywhere that expression is allowed.

Every derived table must have its own alias.
You can use something like
SELECT * FROM b
WHERE a >
(SELECT a FROM b
WHERE a='India')
Or simply use
SELECT * FROM b
WHERE (SELECT a FROM b)
But in this way, you will have more than 1 row.If you will not use Where clause

Related

Using calculation with an an aliased column in ORDER BY

As we all know, the ORDER BY clause is processed after the SELECT clause, so a column alias in the SELECT clause can be used.
However, I find that I can’t use the aliased column in a calculation in the ORDER BY clause.
WITH data AS(
SELECT *
FROM (VALUES
('apple'),
('banana'),
('cherry'),
('date')
) AS x(item)
)
SELECT item AS s
FROM data
-- ORDER BY s; -- OK
-- ORDER BY item + ''; -- OK
ORDER BY s + ''; -- Fails
I know there are alternative ways of doing this particular query, and I know that this is a trivial calculation, but I’m interested in why the column alias doesn’t work when in a calculation.
I have tested in PostgreSQL, MariaDB, SQLite and Oracle, and it works as expected. SQL Server appears to be the odd one out.
The documentation clearly states that:
The column names referenced in the ORDER BY clause must correspond to
either a column or column alias in the select list or to a column
defined in a table specified in the FROM clause without any
ambiguities. If the ORDER BY clause references a column alias from
the select list, the column alias must be used standalone, and not as
a part of some expression in ORDER BY clause:
Technically speaking, your query should work since order by clause is logically evaluated after select clause and it should have access to all expressions declared in select clause. But without looking at having access to the SQL specs I cannot comment whether it is a limitation of SQL Server or the other RDBMS implementing it as a bonus feature.
Anyway, you can use CROSS APPLY as a trick.... it is part of FROM clause so the expressions should be available in all subsequent clauses:
SELECT item
FROM t
CROSS APPLY (SELECT item + '') AS CA(item_for_sort)
ORDER BY item_for_sort
It is simply due to the way expressions are evaluated. A more illustrative example:
;WITH data AS
(
SELECT * FROM (VALUES('apple'),('banana')) AS sq(item)
)
SELECT item AS s
FROM data
ORDER BY CASE WHEN 1 = 1 THEN s END;
This returns the same Invalid column name error. The CASE expression (and the concatenation of s + '' in the simpler case) is evaluated before the alias in the select list is resolved.
One workaround for your simpler case is to append the empty string in the select list:
SELECT
item + '' AS s
...
ORDER BY s;
There are more complex ways, like using a derived table or CTE:
;WITH data AS
(
SELECT * FROM (VALUES('apple'),('banana') AS sq(item)
),
step2 AS
(
SELECT item AS s FROM data
)
SELECT s FROM step2 ORDER BY s+'';
This is just the way that SQL Server works, and I think you could say "well SQL Server is bad because of this" but SQL Server could also say "what the heck is this use case?" :-)

How can i rewrite this lock (FOR UPDATE) subquery in oracle?

The following query is valid in postgres.
SELECT "m".*
FROM (
SELECT * FROM "table" WHERE "column" = 'value' FOR UPDATE
) "m"
FETCH FIRST 2 ROWS ONLY
But in oracle it throws ORA-00907 - MISSING RIGHT PARENTHESIS.
I do not have access to the outer/whole query so I cannot simply write the query without the subquery.
So how can I make a "FROM" subquery work with "FOR UPDATE".
The documented syntax for a SELECT statement is:
select::=
As such, the FOR UPDATE clause MUST be applied to the outer-most sub-query and MUST be the last clause of the statement.
So, to answer your question:
So how can I make a "FROM" subquery work with "FOR UPDATE".
The answer is that it is impossible to make it work as the syntax does not allow it.
If you want to make it work then:
SELECT "m".*
FROM (
SELECT * FROM "table" WHERE "column" = 'value'
) "m"
FOR UPDATE;
db<>fiddle here

SQL GROUP BY 1 2 3 and SQL Order of Execution

This may be a dumb question but I am really confused. So according to the SQL Query Order of Execution, the GROUP BY clause will be executed before the SELECT clause. However it allows to do something like:
SELECT field_1, SUM(field_2) FROM myTable GROUP BY 1
My confusion is that if GROUP BY clause happens before SELECT, in this scenario I provided, how does SQL know what 1 is? It works with ORDER BY clause and it makes sense to me because ORDER BY clause happens after SELECT.
Can someone help me out? Thanks in advance!
https://www.periscopedata.com/blog/sql-query-order-of-operations
My understanding is because it's ordinal notation and for the SELECT statement to pass syntax validation you have to have at least selected a column. So the 1 is stating the first column in the select statement since it knows you have a column selected.
EDIT:
I see people saying you can't use ordinal notation and they are right if you're using SQL Server. You can use it in MySQL though.
select a,b,c from emp group by 1,2,3. First it will group by column a then b and c. It works based on the column after the select statement.
Each GROUP BY expression must contain at least one column that is not an outer reference. You cannot group by 1 if it is not a column in your table.

Not Able to Query Multiple Times from Multiple Common Table Expressions (WITH)?

I was doing some querying today in T-SQL, SQL-Server-2008 and stumbled upon something weird that I didn't understand. Using the query windows, I am trying to query from two common table expressions like so (I stripped out a lot of code to make it more obvious what I was doing):
;WITH temp1 AS (SELECT * FROM dbo.Log)
, temp2 AS (SELECT * FROM dbo.SignalCodeItems300_tbl)
SELECT * FROM temp1
SELECT * FROM temp2
However, only one of the select statements will run, the FIRST one. Regardless of which is which, only the first runs. I assume this is some sort of syntax thing that I'm missing maybe? I get the error "Invalid object name 'temp2'".
Could someone shed some light on this problem? Are there any workarounds for this?
No, this works as it should. A CTE (Common Table Expression) is only available for the first statement after the definition. So in other words, after select * from temp1, they both become unavailable.
The fix would be this:
;WITH temp1 AS (SELECT * FROM dbo.Log)
SELECT * FROM temp1
;WITH temp2 AS (SELECT * FROM dbo.SignalCodeItems300_tbl)
SELECT * FROM temp2
You might want to take a look at the MSDN documentation.
Especially:
Multiple CTE query definitions can be defined in a nonrecursive CTE.
The definitions must be combined by one of these set operators:
UNION ALL, UNION, INTERSECT, or EXCEPT.
You cannot mix and match two different schemas though, as this essentially runs as one query.
Use a view or a user-defined, table-valued function to house your query if you don't want to repeat it explicitly.

How to refer to a variable create in the course of executing a query in T-SQL, in the WHERE clause?

What the title says, really.
If I SELECT [statement] AS whatever, why can't I refer to the whatever column in the body of the WHERE clause? Is there some kind of a workaround? It's driving me crazy.
As far as I'm aware, you can't directly do this in SQL Server.
If you REALLY have to use your column alias in the WHERE clause, you can do this, but it seems like overkill to use a subquery just for the alias:
SELECT *
FROM
(
SELECT [YourColumn] AS YourAlias, etc...
FROM Whatever
) YourSubquery
WHERE YourAlias > 2
You're almost certainly better off just using the contents of the original column in your WHERE clause.
It has to do with the way a SELECT statement gets translated into an abstract query tree: the 'whatever' only appears in the query result projection part of the tree, which is above the filtering part of the tree, so the WHERE clause cannot understand the 'whatever'. This is not some internal implementation detail, it is a fundamental behavior of relational queries: the projection of the result occurs after the evaluation of the joins and filters.
IS really trivial to work around the 'problem' by making the hierarchy of the query explicit:
select ...
from (
select [something] as whatever
from ...
) as subquery
WHERE whatever = ...;
A common table expression can also server the same purpose:
with cte as (
select [something] as whatever
from ...)
select ... from cte
WHERE whatever = ...;
It's to do with the order of operations in the select statement. The WHERE clause is evaluated before the SELECT clause so this information isn't available. Although it is available in the ORDER BY clause as this is processed last.
As others have mentioned, a sub-query will get around this problem.