Why my derived table (subquery) can't have an alias - sql

Sorry for this weird question.
I know that sql has a rule which is "Every derived table must have its own alias." For example:
this is wrong:
SELECT ID FROM (SELECT * FROM (SELECT * FROM IDs));
and it should be:
SELECT ID FROM
(SELECT * FROM (SELECT * FROM IDs)
AS alias) AS anotherAlias;
but my code the as alias is wrong:
SELECT ID FROM tableA
WHERE ID NOT IN
(SELECT ID FROM tableB) AS alias;

A table alias is only needed for a subquery in the FROM clause. Such a subquery is sometimes called a derived table. All derived tables need an alias -- in some (but not all) databases.
In the SELECT clause, a subquery can also take an alias -- but that is a column alias.
In your example, the subquery is not representing any result set or value, so an alias is not needed.

Because you're not using the subquery as a table, you're using it as a collection of values in an IN clause within a WHERE clause.
Your first example has the subquery in the FROM clause.

Think about the reason for the alias. If I write:
SELECT x.*
FROM x
INNER JOIN
(
SELECT *
FROM y
) ON x.id = ???
The query breaks down. I can't refer to any fields in my derived table. Obviously this version works:
SELECT x.*
, y.*
FROM x
INNER JOIN
(
SELECT *
FROM y
) y ON x.id = y.id
In the above, I need to alias y so that I can refer to fields in that derived table.
However, the following query does work without an alias as you mention:
SELECT *
FROM x
WHERE id IN (SELECT id FROM y)
So why does that query work? Well, we don't need the field names of anything in the sub-query. We are just referencing it as a list.
That's why if I write:
SELECT *
FROM x
WHERE id IN (SELECT id, id2 FROM y)
I see an error related to too many fields coming from the sub-query. We can only have one.
Does that help?

Related

How to create a select clause using a subquery

I have the following sql statement:
WITH
subquery AS (
select distinct id from a_table where some_field in (1,2,)
)
select id from another_table where id in subquery;
Edit
JOIN is not an option (this is just a reduced example of a bigger query)
But that obviously does not work. The id field exists in both tables (with a different name, but values are the same: numeric ids). Basically what I want to do is filter by the result of the subquery, like a kind of intersection.
Any idea how to write that query in a correct way?
You need a subquery for the second operand of IN that SELECTs from the CTE.
... IN (SELECT id FROM subquery) ...
But I would recommend to rewrite it as a JOIN.
Are you able to join on ID and then filter on the Where clause?
select a.id
from a.table
inner join b.table on a.id = b.id
where b.column in (1,2)
Since you only want the id from another_table you can use exists
with s as (
select id
from a_table
where some_field in (1,2)
)
select id
from another_table t
where exists ( select * from s where s.id=t.id )
But the CTE is really redundant since all you are doing is
select id
from another_table t
where exists (
select * from a_table a where a.id=t.id and a.some_field in (1,2)
)

Snowflake, SQL where clause

I need to write query with where clause:
where
pl.ods_site_id in (select id from table1 where ...)
But if subquery (table1) didn't return anything, where clause doesn't need to include in result query (like it returns TRUE).
How can I do it? (I have snowflake SQL dialect)
You could include a second condition:
where pl.ods_site_id in (select id from table1 where ...) or
not exists (select id from table1 where ...)
This explicitly checks for the subquery returning no rows.
If you are willing to use a join instead, Snowflake supports qualify clause which might come in handy here. You can run this on Snowflake to see how it works.
with
pl (ods_site_id) as (select 1 union all select 5),
table1 (id) as (select 5) --change this to 7 to test if it returns ALL on no match
select a.*
from pl a
left join table1 b on a.ods_site_id = b.id -- and other conditions you want to add
qualify b.id = a.ods_site_id --either match the join condition
or count(b.id) over () = 0; --or make sure there is 0 match from table1

SQL Subquery w/LEFT JOIN causing Invalid Object Error

I have a query with the following structure:
EDIT Original structure of the query wasn't quite representative.
SELECT A
,B
,C
,D
FROM ( SELECT id,A
FROM myTable
WHERE conditions
GROUP BY id,A) MainQuery
LEFT JOIN (SELECT id, B, C
FROM myView
WHERE id IN
(
SELECT DISTINCT id
FROM MainQuery
)
) sub1
ON sub1.B = MainQuery.A
LEFT JOIN (SELECT MainQuery.id, D
FROM myOtherView
WHERE sub1.id IN
(
SELECT DISTINCT id
FROM MainQuery
)
) sub2
ON sub2.D = sub1.C
When I run the query, I get the error message Invalid object name 'MainQuery'. When I comment out the LEFT JOINs and the fields they feed in the SELECT statement, the query runs just fine. I've also tried AS MainQuery, but I get the same result.
I suspect it has something to do with scope. Where I'm trying to SELECT DISTINCT id FROM MainQuery, is MainQuery out of scope for the WHERE subquery within sub1?
For context, I've been tasked with rewriting a query that used temp tables into a query that can be used in a report deployed on SSRS 2000. My MainQuery, sub1, and sub2 were temp tables in the original query. Those temp tables used subqueries within them, which I've preserved in my translation. But the original query had the advantage of creating each temp table separately, and then joining the results. Temp tables and subqueries are new to me, so I'm not sure how to adapt between the two, or if that's even the right approach.
The SQL for your MainQuery is invalid. Run it by itself and see:
SELECT A, id
FROM myTable
WHERE conditions
GROUP BY A
You can't select A and id, but only group by A. Either you need to also group by id, or wrap id in an aggregate function like min, or max.
With that addressed it looks like your other issue is that you say "LEFT JOIN" but then place the column of your LEFT JOINED table on the left hand side of your where clause. See below where I flip sub1.B and MainQuery.A in the JOIN.
SELECT A
,B
,C
,D
FROM ( SELECT A, id
FROM myTable
WHERE conditions
GROUP BY A,id) MainQuery
LEFT JOIN nutherTable sub1
on MainQuery.A = sub1.B
and MainQuery.id = sub1.id
LEFT JOIN (SELECT D ...) sub2
ON sub1.C = sub2.D

Can we write subquery in between SELECT and FROM

i want to know, how to write subquery in between SELECT and FROM as
SELECT Col_Name,(Subquery)
From Table_Name
Where Some_condition
This:
SELECT y.col_name,
(SELECT x.column
FROM TABLE x) AS your_subquery
FROM TABLE y
WHERE y.col = ?
...is a typical subquery in the SELECT clause. Some call it a "subselect". This:
SELECT y.col_name,
(SELECT x.column
FROM TABLE x
WHERE x.id = y.id) AS your_subquery
FROM TABLE y
WHERE y.col = ?
...is a correlated subquery. It's correlated because the subquery result references a table in the outer query (y in this case).
Effectively, just write whatever additional SELECT statement you want in the SELECT clause, but it has to be surrounded by brackets.
you can do it, but you must use an alias for the subquery
SELECT Col_Name,(Subquery) as S
From Table_Name
Where Some_condition

How can I reference a single table multiple times in the same query?

Sometimes I need to treat the same table as two separate tables. What is the solution?
You can reference, just be sure to use a table alias
select a.EmployeeName,b.EmployeeName as Manager
from Employees A
join Employees B on a.Mgr_id=B.Id
Use an alias like a variable name in your SQL:
select
A.Id,
A.Name,
B.Id as SpouseId,
B.Name as SpouseName
from
People A
join People B on A.Spouse = B.id
Use an alias:
SELECT t1.col1, t2.col3
FROM tbl t1
INNER JOIN tbl t2
ON t1.col1 = t2.col2
Alias is the most obvious solution
SELECT * FROM x1 AS x,y1 AS y
However if the table is the result of a query a common table expressions is quite usefull
;WITH ctx AS
( select * from z)
SELECT y.* FROM ctx AS c1,ctx AS c2
A third solution -- suitable when your query lasts a long time -- is temporary tables:
SELECT *
INTO #monkey
FROM chimpanzee
SELECT * FROM #monkey m1,#monkey m2
DROP TABLE #MONKEY
Note a common table expression is only available for one query (the query directly after it), and temporary tables last for the whole batch.