Setting variables in snowflake - snowflake-cloud-data-platform

I want to define variables before a CTE table and after a CTE table because some variables are dependent on the result of the CTE table. For example
SET(K,B) = (5,2);
with my_data(Key,Index,Value) as (
-- data table as cte
select * from values
(1, 3, 10),
(1, 5, 18),
(1, 14, 4),
(2, 2, 11),
(2, 13, 24),
(2, 29, 40)
)
SELECT VALUE + $K
FROM my_data
This examples works perfectly. But this code:
SET(K,B) = (5,2);
with my_data(Key,Index,Value) as (
-- data table as cte
select * from values
(1, 3, 10 ),
(1, 5, 18 ),
(1, 14, 4 ),
(2, 2, 11 ),
(2, 13, 24),
(2, 29, 40)
)
SET AVG_VAL = (SELECT AVG(VALUE) FROM my_data);
SELECT VALUE + $AVG_VAL
FROM my_data
doesn't because snowflake gives me this error
"SQL compilation error: syntax error line 34 at position 0 unexpected 'SET'."
Should I create a temporary table to store the result of this query (SELECT AVG(VALUE) FROM my_data) in it and then include/use this temporary table for future queries instead of a variable?

Your "CTE" is not a standalone "thing" it only exist in the context of a SELECT.
Thus
WITH cte_x AS (...)
SELECT * FROM cte_x
is one SELECT which has a CTE attached to it.
Thus for you variable assignment the CTE has to be "IN" the paren's
with my_data(Key,Index,Value) as (
select * from values
(1, 3, 10 ),
(1, 5, 18 ),
(1, 14, 4 ),
(2, 2, 11 ),
(2, 13, 24),
(2, 29, 40)
)
SELECT AVG(VALUE) FROM my_data;
AVG(VALUE)
17.833333
given that is a discrete chunk of SQL, that can be captured into the variable:
set AVG_VAL = (
with my_data(Key,Index,Value) as (
select * from values
(1, 3, 10 ),
(1, 5, 18 ),
(1, 14, 4 ),
(2, 2, 11 ),
(2, 13, 24),
(2, 29, 40)
)
SELECT AVG(VALUE) FROM my_data
);
status
Statement executed successfully.
now we can use that value:
select $AVG_VAL * 2;
$AVG_VAL * 2
35.666666
But the next query:
SELECT VALUE + $AVG_VAL
FROM my_data
002003 (42S02): SQL compilation error:
Object 'MY_DATA' does not exist or not authorized.
has no CTE called my_data, so that need to be insert:
with my_data(Key,Index,Value) as (
select * from values
(1, 3, 10 ),
(1, 5, 18 ),
(1, 14, 4 ),
(2, 2, 11 ),
(2, 13, 24),
(2, 29, 40)
)
SELECT VALUE + $AVG_VAL
FROM my_data
If you want a table that can be "used twice" you will need an actual table, at which point I would suggest a temporary table so it only have context in this session.
Which the nature of Pankaj's answer (ether via a permanent or temp table)

This can be done as in -
select * from d2;
+-----+-----+
| ID1 | ID2 |
|-----+-----|
| 1 | 2 |
| 100 | 2 |
| 3 | 4 |
| 300 | 4 |
+-----+-----+
Setting variable -
set (var1) = (select sum(id2) from d2);
+----------------------------------+
| status |
|----------------------------------|
| Statement executed successfully. |
+----------------------------------+
Using variable -
select id1+$var1 from d2;
+-----------+
| ID1+$VAR1 |
|-----------|
| 13 |
| 112 |
| 15 |
| 312 |
+-----------+

An alternatvie approach is to simply use windowed AVG function:
with my_data(Key,Index,Value) as (
-- data table as cte
select * from values
(1, 3, 10),
(1, 5, 18),
(1, 14, 4),
(2, 2, 11),
(2, 13, 24),
(2, 29, 40)
)
SELECT VALUE, AVG(VALUE) OVER(),
VALUE + AVG(VALUE) OVER()
FROM my_data;
Output:
OVER() means that the window used to compute average spans over all rows.

Related

How to create a columns based on other columns SQL Server 2012

I have 2 tables #Claims and #ClaimsActivity:
Query:
declare #Claims table (ClaimID int)
insert into #Claims
values (6070), (6080)
declare #ClaimsActivity table
(
Activityid int,
ClaimID int,
Activity int,
ActivityDate datetime,
ClaimStatus int
)
insert into #ClaimsActivity
values (1, 6070, 0, '2017-11-05 20:23:16.640', 0),
(3, 6070, 6, '2017-11-06 13:50:28.203', 0),
(4, 6070, 9, '2017-11-07 13:39:28.410', 0),
(5, 6070, 10, '2017-11-07 13:40:49.980', 0),
(7, 6070, 8, '2017-11-07 15:46:18.367', 1),
(8, 6070, 8, '2017-11-07 16:50:49.543', 1),
(9, 6070, 9, '2017-11-07 16:50:54.733', 0),
(10, 6070, 4, '2017-11-07 16:55:22.135', 0),
(11, 6070, 6, '2017-11-08 18:32:15.101', 0),
(12, 6080, 0, '2017-11-12 11:15:17.199', 0),
(13, 6080, 8, '2017-11-13 09:12:23.203', 1)
select *
from #Claims
select *
from #ClaimsActivity
order by ActivityDate
I need to add 2 columns based on data in #ClaimsActivity: IsReopened and DateReopened
The logic is:
If the last ClaimStatus (based on ActivityDate) = 1 then IsReopened = 0
But if the last ClaimStatus = 0 then it need to go and check whether one of the Activity is = 9 (Claim Reopened)
and if one of the Activity = 9 then IsReopened should = 1 and DateReopened should be the last date when it was reopened
I brought column StatusOfClaim, but I also need IsReopened and DateReopened
select
Claimid,
isnull((select top 1
case when al.ClaimStatus = 1
then 'Closed'
else 'Open'
end
from
#ClaimsActivity al
where
C.ClaimID = al.ClaimID
order by
al.ActivityDate desc), 'Open') as 'Status of Claim',
NULL as 'isReopen',
NULL as 'DateReopened'
from
#Claims c
Desired output should be like this:
There are many different ways you can accomplish this, but here is an example using CROSS APPLY and OUTER APPLY:
SELECT
ClaimID,
CASE WHEN tmp.IsOpen = 1 THEN 'Open' ELSE 'Closed' END AS 'Status of Claim',
CASE WHEN tmp.IsOpen = 1 AND lastReopen.Activityid IS NOT NULL THEN 1 ELSE 0 END AS 'isReopen',
lastReopen.ActivityDate AS 'DateReopened'
FROM #Claims c
CROSS APPLY (
SELECT ISNULL((
SELECT TOP 1 CASE WHEN al.ClaimStatus = 1 THEN 0 ELSE 1 END
FROM #ClaimsActivity al
WHERE c.ClaimID = al.ClaimID
ORDER BY al.ActivityDate DESC
), 1) AS IsOpen
) tmp
OUTER APPLY (
SELECT TOP 1
al.Activityid,
al.ActivityDate
FROM #ClaimsActivity al
WHERE c.ClaimID = al.ClaimID AND al.Activity = 9
ORDER BY al.ActivityDate DESC
) lastReopen
The CROSS APPLY is just used to produce a column that tells us whether a claim is open or closed, and we can reuse this throughout the rest of the query.
The OUTER APPLY is used to grab to the last "reopen" activity for each claim, of which you want the date.
I can't attest to the performance of this query, but this should at least give you the correct results.

SQL Pivot Using three tables

I am running into some trouble trying to pivot some data out of SQL.
I have three tables that will comprise the data.
Table 1: (Clause)
-Clause
-ClauseName
Table 2: (Process)
-Id
-ProcessName
Table 3: (RELProcessClauses)
-ProcessId
-Clause
-WeightedValue
Ultimately, I am looking to have a matrix of data that is Clause, ClauseName down the left, ProcessName across the top and the Weighted value to correspond between Process and Clause.
Not sure if this will make much sense.
Join the three tables and use PIVOT on it. You can run the following query:
SELECT * FROM (
SELECT
c.Clause,
c.ClauseName,
p.ProcessName,
pc.WeightedValue
from RELProcessClauses pc
JOIN Clause c on pc.clause = c.clause
JOIN Process p on pc.ProcessId = p.id
) x
PIVOT (
SUM(WeightedValue)
FOR ProcessName IN ([ProcessName1], [ProcessName2], [ProcessName3])
) as pvt
Output table:
+--------+-------------+--------------+--------------+--------------+
| Clause | ClauseName | ProcessName1 | ProcessName2 | ProcessName3 |
+--------+-------------+--------------+--------------+--------------+
| 1 | ClauseName1 | 10 | 15 | 30 |
| 2 | ClauseName2 | 15 | 20 | 30 |
| 3 | ClauseName3 | 20 | 20 | 30 |
+--------+-------------+--------------+--------------+--------------+
The query/output works on the demo tables created using the query below:
CREATE TABLE Clause (
Clause int,
ClauseName varchar(255)
);
CREATE TABLE Process (
Id int,
ProcessName varchar(255)
);
CREATE TABLE RELProcessClauses (
ProcessId int,
Clause int,
WeightedValue int
);
INSERT INTO Clause VALUES
(1, 'ClauseName1'),
(2, 'ClauseName2'),
(3, 'ClauseName3');
INSERT INTO Process VALUES
(1, 'ProcessName1'),
(2, 'ProcessName2'),
(3, 'ProcessName3');
INSERT INTO RELProcessClauses VALUES
(1, 1, 10),
(1, 2, 15),
(1, 3, 20),
(2, 1, 15),
(2, 2, 20),
(2, 3, 20),
(3, 1, 30),
(3, 2, 30),
(3, 3, 30);

Update rows based on previous value of same column. (MSSQL2008)

I have a table with one column:
ColA Rownumb
1 1
1 2
1 3
2 4
1 5
1 6
1 7
2 8
2 9
2 10
2 11
2 12
2 13
2 14
3 15
2 16
2 17
2 18
3 19
3 20
3 21
3 22
3 23
3 24
On row 4 the value of ColA changes for the first time. From row 8 it has changed permanently in the sense that following rows also have value 2. I want to update rows 5, 6 and 7 so that value 1 becomes 2. The same logic goes for rows 16, 17 and 18. In that case I want to update ColA from 2 to 3.
So how do I updated the rows as specified above?
Thanks.
Since you area using SQL-Server-2008 you can do this with recursive common table expression:
DECLARE #DataSource TABLE
(
[ColA] INT
,[Rownumb] INT
);
INSERT INTO #DataSource ([ColA], [Rownumb])
VALUES (1, 1), (1, 2), (1, 3), (2, 4), (1, 5), (1, 6), (1, 7), (2, 8), (2, 9), (2, 10), (2, 11), (2, 12), (2, 13), (2, 14), (3, 15), (2, 16), (2, 17), (2, 18), (3, 19), (3, 20), (3, 21), (3, 22), (3, 23), (3, 24);
WITH DataSourceRecursive AS
(
SELECT [ColA]
,[Rownumb]
FROM #DataSource
WHERE [Rownumb] = 1
UNION ALL
SELECT CASE WHEN DS1.[ColA] < DSR.[ColA] THEN DSR.[ColA] ELSE DS1.[ColA] END
,DS1.[Rownumb]
FROM #DataSource DS1
INNER JOIN DataSourceRecursive DSR
ON DS1.[Rownumb] = DSR.[RowNumb] + 1
)
SELECT *
FROM DataSourceRecursive;

Select query using variable not running in mssql

Select query is not working when use variable in MSSQL2014
My Schema is :-
CREATE TABLE product
(idproduct int, name varchar(50), description varchar(50), tax decimal(18,0))
INSERT INTO product
(idproduct, name, description,tax)
VALUES
(1, 'abc', 'This is abc',10),
(2, 'xyz', 'This is xyz',20),
(3, 'pqr', 'This is pqr',15)
CREATE TABLE product_storage
(idstorage int,idproduct int,added datetime, quantity int, price decimal(18,0))
INSERT INTO product_storage
(idstorage,idproduct, added, quantity,price)
VALUES
(1, 1, 2010-01-01,0,10.0),
(2, 1, 2010-01-02,0,11.0),
(3, 1, 2010-01-03,10,12.0),
(4, 2, 2010-01-04,0,12.0),
(5, 2, 2010-01-05,10,11.0),
(6, 2, 2010-01-06,10,13.0),
(7, 3, 2010-01-07,10,14.0),
(8, 3, 2010-01-07,10,16.0),
(9, 3, 2010-01-09,10,13.0)
and i am executing below command:-
declare #price1 varchar(10)
SELECT p.idproduct, p.name, p.tax,
[#price1]=(SELECT top 1 s.price
FROM product_storage s
WHERE s.idproduct=p.idproduct AND s.quantity > 0
ORDER BY s.added ASC),
(#price1 * (1 + tax/100)) AS [price_with_tax]
FROM product p
;
This is not working in MSSQL, Please Help me out.
for detail check http://sqlfiddle.com/#!6/91ec2/296
And My query is working in MYSQL
Check for detail :- http://sqlfiddle.com/#!9/a71b8/1
Try this query
SELECT
p.idproduct
, p.name
, p.tax
, (t1.price * (1 + tax/100)) AS [price_with_tax]
FROM product p
inner join
(
SELECT ROW_NUMBER() over (PARTITION by s.idproduct order by s.added ASC) as linha, s.idproduct, s.price
FROM product_storage s
WHERE s.quantity > 0
) as t1
on t1.idproduct = p.idproduct and t1.linha = 1
Try it like this:
Explanantion: You cannot use a variable "on the fly", but you can do row-by-row calculation in an APPLY...
SELECT p.idproduct, p.name, p.tax,
Price.price1,
(price1 * (1 + tax/100)) AS [price_with_tax]
FROM product p
CROSS APPLY (SELECT top 1 s.price
FROM product_storage s
WHERE s.idproduct=p.idproduct AND s.quantity > 0
ORDER BY s.added ASC) AS Price(price1)
;
EDIT: Your Fiddle uses a bad literal date format, try this:
INSERT INTO product_storage
(idstorage,idproduct, added, quantity,price)
VALUES
(1, 1, '20100101',0,10.0),
(2, 1, '20100102',0,11.0),
(3, 1, '20100103',10,12.0),
(4, 2, '20100104',0,12.0),
(5, 2, '20100105',10,11.0),
(6, 2, '20100106',10,13.0),
(7, 3, '20100107',10,14.0),
(8, 3, '20100108',10,16.0),
(9, 3, '20100109',10,13.0)
Here is the correct schema for SQL Server and query runs perfect as Shnugo Replied.
VALUES
(1, 1, convert(datetime,'2010-01-01'),0,10.0),
(2, 1, convert(datetime,'2010-01-02'),0,11.0),
(3, 1, convert(datetime,'2010-01-03'),10,12.0),
(4, 2, convert(datetime,'2010-01-04'),0,12.0),
(5, 2, convert(datetime,'2010-01-05'),10,11.0),
(6, 2, convert(datetime,'2010-01-06'),10,13.0),
(7, 3, convert(datetime,'2010-01-07'),10,14.0),
(8, 3, convert(datetime,'2010-01-07'),10,16.0),
(9, 3, convert(datetime,'2010-01-09'),10,13.0)

Creating blocks within a CTE - SQL Server

I am trying to work out how I can tag unique (what i am calling) blocks (or segments if you will) which have a start and end based consecutive 'Trip' rows ordered by 'epoch' sharing the same 'code'. In this case group by 'trip', 'code' will not work as I need to measure the duration of the 'code' remains constant for the trip. I've tried to use a CTE but I have been unable to partition the data in such a way that it gives desired result shown below. The block number I've shown could be any value, just so long as it is unique so that it tags the consecutive occurrences of the same 'code' on the trip in order of 'epoch'.
Any ideas?
declare #data table (id int, trip int, code int NULL, epoch int, value1 int, value2 int);
insert into #data (id, trip, code, epoch, value1, value2)
values
(1, 1, null, 31631613, 0, 0),
(2, 2, 1, 31631614, 10, 40),
(3, 1, 1, 31631616, 10, 60),
(4, 1, 1, 31631617, 40, 60),
(5, 2, 1, 31631617, 23, 40),
(6, 2, 2, 31631620, 27, 40),
(7, 2, 2, 31631629, 23, 40),
(9, 1, 1, 31631618, 39, 60),
(10, 1, null, 31631621, 38, 60),
(12, 1, null, 31631625, 37, 60),
(15, 1, null, 31631627, 35, 60),
(19, 1, 1, 31631630, 39, 60),
(20, 1, 1, 31631632, 40, 60),
(21, 2, 1, 31631629, 23, 40);
block id trip code epoch value1 value2
1 1 1 NULL 31631613 0 0
2 2 2 1 31631614 10 40
2 5 2 1 31631617 23 40
3 3 1 1 31631616 10 60
3 4 1 1 31631617 40 60
3 9 1 1 31631618 39 60
4 6 2 2 31631620 27 40
4 7 2 2 31631629 23 40
5 10 1 NULL 31631621 38 60
5 12 1 NULL 31631625 37 60
5 15 1 NULL 31631627 35 60
6 19 1 1 31631630 39 60
6 20 1 1 31631632 40 60
7 21 2 1 31631629 23 40
You didn't update your expected output so I'm still not 100% sure this is what you want, but give it a try...
SELECT
DENSE_RANK() OVER (ORDER BY trip, code),
*
FROM
#data
ORDER BY
trip, code, epoch
Ok, it's far from perfect by any means but it is a starter that at least identifies the start and end of a contiguous block where the 'code' has remained the same for the trip. For the sake of at least contributing something I'll post what I jerried up. If I ever get time to do a proper job I'll post it.
declare #minint int; set #minint = -2147483648;
declare #maxint int; set #maxint = 2147483647;
declare #id_data table (pk int IDENTITY(1,1), id int, trip int, code int NULL, epoch int, value1 int, value2 int);
insert into #id_data VALUES(#minint, #minint, #minint, #minint, #minint, #minint);
insert into #id_data
SELECT id, trip, coalesce(code,0), epoch, value1, value2
FROM #data
order by trip, epoch, code;
insert into #id_data VALUES(#maxint, #maxint, #maxint, #maxint, #maxint, #maxint);
WITH CTE as
(
SELECT pk, id, trip, code, epoch, value1, value2, ROW_NUMBER() OVER (PARTITION BY trip ORDER BY epoch) as row_num
FROM #id_data
)
SELECT B.*, A.code, C.min_next_code
FROM CTE A
INNER JOIN CTE B ON (B.pk = A.pk + 1) AND (A.code != B.code) -- SELECTS THE RECORDS THAT START A NEW GROUP
OUTER APPLY (
SELECT min_next_code = MIN(pk) - 1 -- LOCATION OF NEXT GROUP
FROM CTE
WHERE pk > B.pk AND (trip = B.trip) AND (code != B.code)
) C
WHERE B.id < #maxint

Resources