In Snowflake, how to reference CTE in following table - snowflake-cloud-data-platform

We have the following working query in snowflake:
with
latest_track_metrics as (
select * from track_metrics
where "week_id" = (select max("week_id") from track_metrics)
)
select * from latest_track_metrics
in an effort to clean this code up a bit, we'd love to refactor the select max("week_id") from track_metrics into its own row with variable name, as such:
with
max_weekid as (select max("week_id") from track_metrics),
latest_track_metrics as (
select * from track_metrics
where "week_id" = max_weekid // error on this line, not recognizing max_weekid
)
select * from latest_track_metrics
However the latter query returns the error Error: invalid identifier 'MAX_WEEKID' (line 5). We've tried to wrap the max_weekid with parenthesis, quotes, backticks, etc. all with no luck. Is it possible to call a CTE in this manner?

Using QUALIFY and windowed MAX:
with latest_track_metrics as (
select *
from track_metrics
qualify "week_id" = max("week_id") over()
)
select *
from latest_track_metrics;

Treat max_weekid as a subquery or a table.
Instead of WHERE week_id = , you should be joining it together with track_metrics with a JOIN clause.
Perhaps something like this:
with
max_weekid as (select max("week_id") AS M from track_metrics),
latest_track_metrics as (
select * from track_metrics
inner join max_weekid
on track_metrics.week_id = max_weekid.M)
select * from latest_track_metrics

The error is that you are matching a column "week_id" to a CTE "max_weekid". SQL should be
with
max_weekid as (select max("week_id") as max_week_id from track_metrics),
latest_track_metrics as (
select * from track_metrics
where "week_id" = max_weekid.max_week_id // CTE.column
)
select * from latest_track_metrics

Related

Snowflake using CTE to create table

Does Snowflake allow you to create a series of CTEs then join them together at the end to create a table?
For example:
with CTE1 as ( SELECT * FROM TABLE1)
,CTE2 AS (SELECT * FROM TABLE2)
,CTE3 AS (SELECT * FROM TABLE3)
CREATE TABLE TABLE_NAME_HERE AS
SELECT * FROM CTE1 AS 1
LEFT JOIN CTE2 AS 2 ON 1.KEY = 2.KEY
LEFT JOIN CTE3 AS 3 ON 1.KEY = 3.KEY
I'm getting a unexpected 'CREATE'. error
It's an interesting one, because that CTAS (CREATE TABLE AS) form is
CREATE TABLE <name> AS <select>
and SELECT form is can have a CTE and a CTE form is
WITH <name> AS <select>
CTE can also be in sub-select given "it's just a select", thus
SELECT <columns>
FROM (
WITH cte_1 AS (
SELECT <columns>
FROM table
)
SELECT <columns>
FROM cte_1
)
which shows why the form that Lukasz shows is correct because if we add more parens
CREATE TABLE name AS (
WITH cte_1 AS (
SELECT <columns>
FROM table
)
SELECT <columns>
FROM cte_1
)
Yes, it is possible:
CREATE TABLE TABLE_NAME_HERE AS
WITH CTE1 as ( SELECT * FROM TABLE1)
,CTE2 AS (SELECT * FROM TABLE2)
,CTE3 AS (SELECT * FROM TABLE3)
SELECT * -- here should be explicit column list to avoid name duplication error
FROM CTE1 AS 1
LEFT JOIN CTE2 AS 2 ON 1.KEY = 2.KEY
LEFT JOIN CTE3 AS 3 ON 1.KEY = 3.KEY;

Does Cte in Sql server executed once or each time it is referenced?

I have a dummy table
create table emp
(
id int,
name varchar,
sal int
)
with cte1 as(
select * from emp where name like 'a%'
),
cte2 as (
select * from cte1 where sal > 100
),
cte3 as (
select * from cte1 where id != 1223
)
select * from cte2
union all
select * from ct3
Does using cte in this manner guarantee that cte1, cte2, cte3 will be run once only?
Though I can check the query plan but will that guarantee that the behaviour will not change if the data is changed and next time optimzer decides to do it in another way?
"Does using cte in this manner guarantee that cte1, cte2, cte3 will be run once only?" No. The query you have here will not "run" cte1 once, it'll run it twice. This can be seen in the query's query plan where you can see emp appears twice.
This is because, in the final SELECT you reference both cte3 and cte2 in the final select. Both of these reference cte1, which in turn references emp. As a result the call to cte3 and cte2 results in 2 calls to the object emp.
Technically, if you wanted, you could rewrite the above to the below; which might help you see why emp is referenced twice:
SELECT *
FROM (SELECT *
FROM (SELECT *
FROM emp
WHERE [name] LIKE 'a%') AS cte1
WHERE sal > 100) AS cte2
UNION ALL
SELECT *
FROM (SELECT *
FROM (SELECT *
FROM emp
WHERE [name] LIKE 'a%') AS cte1
WHERE id != 1223) AS cte3;

MSSQL subquery result to show and calculate

I need show a subquery result and use this same result to calculate other value, is possible set this value in a variable in MS SQL 2008 or something like this?
exemple:
SELECT
#test = (SELECT COUNT(*) FROM [tableTest] WHERE [tableTest].[columnA] = [tableA].[columnA]) as 'Counter'
, (#test * 50) as 'Calc'
, [tableA].[columnA]
FROM tableA
you may use a cte and join on it.
with cte as (select count(*) cnt, columnA from [tableTest] group by columnA)
select
c.cnt as 'Counter',
c.cnt * 50 as 'Calc',
a.columnA
from tableA a
join cte c on c.columnA = a.columnA
It could also be done with a subquery, of course
select
a.columnA,
c.cnt as 'Counter',
c.cnt * 50 as 'Calc'
from tableA a
join (select columnA, count(*) as cnt
from tableTest
group by columnA) c
on c.columnA = a.columnA
Why can't you do this. Move the subquery outside of select statement and store the result in a variable
Then use that variable for calculations.
declare #test int = (SELECT COUNT(*) FROM [tableTest])
SELECT
#test as 'Counter'
, (#test * 50) as 'Calc'
, [tableA].[columnA]
FROM tableA
Update :
SELECT [Counter],
( [Counter] * 50 ) AS 'Calc',
columnA
FROM (SELECT (SELECT Count(*)
FROM [tableTest]
WHERE [tableTest].[columnA] = [tableA].[columnA]) AS 'Counter',
[tableA].[columnA]
FROM tableA) A
You can also use correlated sub-queries:
SELECT
Counter = (SELECT COUNT(*) FROM tableTest t WHERE t.columnA = a.columnA),
Calc = (SELECT COUNT(*) FROM tableTest t WHERE t.columnA = a.columnA) * 50,
a.columnA
FROM tableA a
It'll be optimized to be only evaluated once.
Try:
SELECT t2.[Count]
,t2.[Count] * 50 As [Calc]
,[tableA].[columnA]
FROM TableA
CROSS APPLY
(
SELECT COUNT(*) AS [Count]
FROM TableTest
WHERE [tableTest].[columnA] = [tableA].[columnA]
) t2

autonumber in select statement in SQL Server

I would like to create a select query statement with autonumber.. like..
select * from tbl1
will give me everything from table.
The result I'd like to get is..
1 data
2 data
3 data
So how can I do to get that number..??
like..
select (for autonumber), * from tbl1
the data in my table will repeated (no unique data)
Use ROW_NUMBER:
SELECT ROW_NUMBER() OVER (ORDER BY col1) AS rn, * FROM tbl1
To filter the results based on the row number use this:
SELECT * FROM
(
SELECT ROW_NUMBER() OVER (ORDER BY col1) AS rn, * FROM tbl1
) T1
WHERE rn = 5
You may need to find the identity's offset e.g. last ID of second table:
DECLARE #lastAutoID int
SET #lastAutoID = abs(( Select max(convert(float,[ConsID]))
FROM [RXPIPEDB]...[consumption] ) )
Then use ROW_NUMBER():
#lastAutoID + ROW_NUMBER() OVER (ORDER BY oldICN_str)

Can a query be used in place of a table in SQL Server

This should really be allowed - I do not understand why it is not.
SELECT *
FROM (
SELECT *
FROM MyTable
)
In SQL Server it is allowed, but the inner select has to be given a name, such as:
SELECT *
FROM (
SELECT *
FROM MyTable
) m
When a name is not supplied it will throw an incorrect syntax error near ')' message.
If you add a table alias it should work:
SELECT *
FROM (
SELECT *
FROM MyTable
) as A
You are missing an 'alias' on the sub-query
(I added an alias 'X' )
SELECT *
FROM (
SELECT *
FROM MyTable
) X
There are at least two ways to accomplish this, but what you might be looking for is a Common Table Expression (CTE), introduced in SQL Server 2005.
From the above link:
USE AdventureWorks;
GO
WITH Sales_CTE (SalesPersonID, NumberOfOrders, MaxDate)
AS
(
SELECT SalesPersonID, COUNT(*), MAX(OrderDate)
FROM Sales.SalesOrderHeader
GROUP BY SalesPersonID
)
SELECT E.EmployeeID, OS.NumberOfOrders, OS.MaxDate,
E.ManagerID, OM.NumberOfOrders, OM.MaxDate
FROM HumanResources.Employee AS E
JOIN Sales_CTE AS OS
ON E.EmployeeID = OS.SalesPersonID
LEFT OUTER JOIN Sales_CTE AS OM
ON E.ManagerID = OM.SalesPersonID
ORDER BY E.EmployeeID;
GO
Alternately, you can create a View, which is a permanent table-shaped representation of a query that you can access by name:
USE AdventureWorks ;
GO
IF OBJECT_ID ('hiredate_view', 'V') IS NOT NULL
DROP VIEW hiredate_view ;
GO
CREATE VIEW hiredate_view
AS
SELECT c.FirstName, c.LastName, e.EmployeeID, e.HireDate
FROM HumanResources.Employee e JOIN Person.Contact c on e.ContactID = c.ContactID ;
GO
SELECT * FROM hiredate_view

Resources