Table Aliases into Subqueries - sql

(Submitting here to assist other Snowflake Users who may run into similar challenges... Interested to see if there are any additional recommendations beyond what's bee provided already.)
Why doesn't table alias work into subqueries?
I was using a sample table select query but it doesn't work when I coded a table alias.
select * from SNOWFLAKE_SAMPLE_DATA.TPCDS_SF100TCL.STORE as t
where
t.S_REC_START_DATE = (
select max(i.S_REC_START_DATE) from t as i
where i.S_REC_START_DATE < '2000-01-01'
)
I got a SQL compilation error: Object 'T' does not exist.
is not possible to use table alias?

(Previously provided by Mike Walton, a tenured member of Snowflake's Professional Services team)
You can, but not that way. You should use a CTE, instead:
WITH t as (
select * from SNOWFLAKE_SAMPLE_DATA.TPCDS_SF100TCL.STORE
)
select * FROM t
where t.S_REC_START_DATE = (
select max(S_REC_START_DATE) as S_REC_START_DATE from t
where S_REC_START_DATE < '2000-01-01'
)
Any other ideas and/or recommendations?

Related

SQL Many-to-Many Query in Apex Oracle

I created a many-to-many relationship between the tables PRODUCT_TYPE and LABEL by creating and intermediate table PRODUCT_TYPE-LABELS. I wanted to retrieve all the products that have the labels 'Gluten free' and 'Lactose free' and found help on a similar subject (How to filter SQL results in a has-many-through relation) but never got it to work properly.
The tables are as follows:
PRODUCT_TYPE{
PRODUCT_TYPE ->Primary Key
CONTAINER
VOLUME_L
PRICE_EUROS
...
}
LABEL{
LABEL_NAME ->Primary Key
REQUIREMENTS
}
PRODUCT_TYPE-LABELS{
PRODUCT_TYPE
LABEL_NAME
}
In fact, even when creating the simplest command
SELECT PRODUCT_TYPE-LABELS.PRODUCT_TYPE
FROM PRODUCT_TYPE-LABELS
I obtain the following error ORA-00933: SQL command not properly ended that I can't solve. I'm working on Apex Oracle (Required for this course).
Thanks !
If your table is really named PRODUCT_TYPE-LABELS then you need to enclose it within double quotes. - is not a standard character that is allowed in table names so to use special characters such as that, you need to put quotes around the table. I would recommend AGAINST using a table name such as that and maybe use something like PRODUCT_TYPE_LABEL.
Does the query work from SQL Developer, SQLPlus or any other tool that you can use to run queries?
Try running the query:
SELECT PRODUCT_TYPE
FROM "PRODUCT_TYPE-LABELS"
select * from (
select rownum rowno, CUST_ID,CUST_NAME,no_of_orders,orders_amount,EMAIL from (
select mst.CUST_ID, mst.CUST_NAME, count(mst.INVONO) no_of_orders, sum(SUB_TOTAL) orders_amount, au.EMAIL
from sales_mst mst,CUSTOMER_INFO cust, APP_USERS au
where cust.USER_NAME = au.USERNAME
and cust.cust_id=mst.cust_id
and (au.gender= :P41_GENDER or :P41_GENDER is null)
and (au.DOB = :P41_DOB or :P41_DOB is null)
group by mst.CUST_ID, mst.CUST_NAME,au.EMAIL
having nvl(sum(SUB_TOTAL),0) > 0
order by 3 desc,4 desc
)) where rowno <=:P41_TO_CUSTOMER
/*select * from (
select rownum rowno, CUST_ID,CUST_NAME,no_of_orders,orders_amount from (
select mst.CUST_ID, mst.CUST_NAME, count(mst.INVONO) no_of_orders, sum(SUB_TOTAL) orders_amount
from sales_mst mst
group by mst.CUST_ID, mst.CUST_NAME
having nvl(sum(SUB_TOTAL),0) > 0
order by 3 desc,4 desc
)) where rowno <=:P41_TO_CUSTOMER
*/

How do I add where condition?

I want to add a condition according to the returned result
You can't reuse an alias defined in the SELECT clause in the FROM clause of the scope. You need to either repeat the expression, or use a subquery or CTE.
select dayofweek_iso(timestamp(mycol)) as mynewcol
from mytable
where dayofweek_iso(timestamp(mycol)) = 1
Or:
select *
from (
select dayofweek_iso(timestamp(mycol)) as mynewcol
from mytable
) t
where mynewcol = 1
Use your existing query as a subquery:
select * from (
<add your existing query in the screenshot here>
) t
where <add your conditions here>
i think you should include your code in the post itself. A picture like this is quite unhandy.
W3: SQL WHERE
as you can see here, just add your WHERE statement at the end:
SELECT * FROM XXX WHERE CONDITION
Out of interest, since Db2 11.5, you can reference a column alias in a WHERE clause if using NPS (i.e. Netezza compatibility) mode.
CREATE TABLE MYTABLE(MYCOL DATE)
SET SQL_COMPAT='NPS'
SELECT DAYOFWEEK_ISO(TIMESTAMP(MYCOL)) AS MYNEWCOL
FROM MYTABLE
WHERE MYNEWCOL = 1
Still, this is best reserved for users migrating from Netezza as NPS mode changes some other things too.

select latest Table in a Big Query Dataset - Standard SQL syntax

I have dataset containing multiple tables with similar names:
e.g.
affilinet_4221_first_20180911_204956
affilinet_4221_first_20180911_160004
affilinet_4221_first_20180911_085559
affilinet_4221_first_20180910_201323
affilinet_4221_first_20180910_201042
affilinet_4221_first_20180910_080006
affilinet_4221_first_20180909_160707
This query identifies the latest dataset (according to yyyymmdd_hhmmss naming convention) with __TABLES_SUMMARY__ method
SELECT max(table_id) as table_id FROM `modemutti-8d8a6.feed_first.__TABLES_SUMMARY__`
where table_id LIKE "affilinet_4221_first_%"
query result
this query extracts all values from a specific table with _TABLE_SUFFIX method
SELECT * FROM `modemutti-8d8a6.feed_first.*`
WHERE _TABLE_SUFFIX = "affilinet_4221_first_20180911_204956"
query result
This query combines __TABLES_SUMMARY__ (which returns affilinet_4221_first_20180911_204956) and _TABLE_SUFFIX
SELECT * FROM `modemutti-8d8a6.feed_first.*`
WHERE _TABLE_SUFFIX = (
SELECT max(table_id) FROM `modemutti-8d8a6.feed_first.__TABLES_SUMMARY__`
where table_id LIKE "affilinet_4221_first_%")
this query fails:
Error: Cannot read field 'modemio_cat_level' of type INT64 as STRING
error screenshot
any idea why is this happening or how I could solve the issue?
------------EDIT------------
#Mikhail solution works correctly but processes a huge amount of data. See explicit call Vs the suggested Method. Another solution would have been
SELECT * FROM `modemutti-8d8a6.feed_first.affilinet_4221_first_*` WHERE _TABLE_SUFFIX =
(
SELECT MAX(_TABLE_SUFFIX) FROM`modemutti-8d8a6.feed_first.affilinet_4221_first_*`
)
but this processes also a much bigger amount of data compared to the explicit query. Is there are way to achieve through a view in the UI or should I rather use the Python / Java SDK via API?
Try below
#standardSQL
SELECT * FROM `modemutti-8d8a6.feed_first.affilinet_4221_first_*`
WHERE _TABLE_SUFFIX = (
SELECT REPLACE(MAX(table_id), 'affilinet_4221_first_', '')
FROM `modemutti-8d8a6.feed_first.__TABLES_SUMMARY__`
WHERE table_id LIKE "affilinet_4221_first_%"
)

Is there something I can change to make my view run faster?

I just created a view but it is really slow, since my actual table has something around 800k rows.
Is there something I can change in the actual sql code to make it run faster?
Here is how it looks now:
Select B.*
FROM
(Select A.*, (select count(B.KEY_ID)/77
FROM book_new B
where B.KEY_ID = A.KEY_ID) as COUNT_KEY
FROM
(select *
from book_new
where region = 'US'
and (actual_release_date is null or
actual_release_date >= To_Date( '01/07/16','dd/mm/yy'))
) A
) B
WHERE B.COUNT_KEY = 1
OR (B.COUNT_KEY > 1 AND B.NEW_OLD <> 'Old')
The most obvious things to do are add indexes:
Add an index on book_new(key_id)
Add an index on book_new(region, actual_release_date)
These are probably sufficient. It is possible that rewriting the query would help, but this is a good beginning. If you want to rewrite the query, it would help if you described the logic you are trying to implement.
There are many ways to solve this issue based on your needs
You can create an indexed view
You can create an index in the base tables which are used in this view.
You can use the required columns in the SELECT statement instead of using SELECT * FROM,
If the table contains many columns but you require only few columns, you can create a NON CLUSTERED INDEX with INCLUDE COLUMNS option which will reduce the LOGICAL READS.
For starters, replace the scalar subquery for COUNT_KEY with a windowed COUNT(*).
SELECT * FROM
(
select book_new.*, COUNT(*) OVER ( PARTITION BY book_new.key_id)/77 COUNT_KEY
from book_new
where region = 'US'
and (actual_release_date is null or
actual_release_date >= To_Date( '01/07/16','dd/mm/yy'))
)
WHERE count_key = 1 OR ( count_key > 1 AND new_old <> 'Old' )
This way, you only go through the BOOK_NEW table one time.
BTW, I agree with other comments that this query makes little sense.

Alternative SQL ways of looking up multiple items of known IDs?

Is there a better solution to the problem of looking up multiple known IDs in a table:
SELECT * FROM some_table WHERE id='1001' OR id='2002' OR id='3003' OR ...
I can have several hundreds of known items. Ideas?
SELECT * FROM some_table WHERE ID IN ('1001', '1002', '1003')
and if your known IDs are coming from another table
SELECT * FROM some_table WHERE ID IN (
SELECT KnownID FROM some_other_table WHERE someCondition
)
The first (naive) option:
SELECT * FROM some_table WHERE id IN ('1001', '2002', '3003' ... )
However, we should be able to do better. IN is very bad when you have a lot of items, and you mentioned hundreds of these ids. What creates them? Where do they come from? Can you write a query that returns this list? If so:
SELECT *
FROM some_table
INNER JOIN ( your query here) filter ON some_table.id=filter.id
See Arrays and Lists in SQL Server 2005
ORs are notoriously slow in SQL.
Your question is short on specifics, but depending on your requirements and constraints I would build a look-up table with your IDs and use the EXISTS predicate:
select t.id from some_table t
where EXISTS (select * from lookup_table l where t.id = l.id)
For a fixed set of IDs you can do:
SELECT * FROM some_table WHERE id IN (1001, 2002, 3003);
For a set that changes each time, you might want to create a table to hold them and then query:
SELECT * FROM some_table WHERE id IN
(SELECT id FROM selected_ids WHERE key=123);
Another approach is to use collections - the syntax for this will depend on your DBMS.
Finally, there is always this "kludgy" approach:
SELECT * FROM some_table WHERE '|1001|2002|3003|' LIKE '%|' || id || '|%';
In Oracle, I always put the id's into a TEMPORARY TABLE to perform massive SELECT's and DML operations:
CREATE GLOBAL TEMPORARY TABLE t_temp (id INT)
SELECT *
FROM mytable
WHERE mytable.id IN
(
SELECT id
FROM t_temp
)
You can fill the temporary table in a single client-server roundtrip using Oracle collection types.
We have a similar issue in an application written for MS SQL Server 7. Although I dislike the solution used, we're not aware of anything better...
'Better' solutions exist in 2008 as far as I know, but we have Zero clients using that :)
We created a table valued user defined function that takes a comma delimited string of IDs, and returns a table of IDs. The SQL then reads reasonably well, and none of it is dynamic, but there is still the annoying double overhead:
1. Client concatenates the IDs into the string
2. SQL Server parses the string to create a table of IDs
There are lots of ways of turning '1,2,3,4,5' into a table of IDs, but the Stored Procedure which uses the function ends up looking like...
CREATE PROCEDURE my_road_to_hell #IDs AS VARCHAR(8000)
AS
BEGIN
SELECT
*
FROM
myTable
INNER JOIN
dbo.fn_split_list(#IDs) AS [IDs]
ON [IDs].id = myTable.id
END
The fastest is to put the ids in another table and JOIN
SELECT some_table.*
FROM some_table INNER JOIN some_other_table ON some_table.id = some_other_table.id
where some_other_table would have just one field (ids) and all values would be unique