While loop in snowflake - snowflake-cloud-data-platform

I am working on a SQL script below on Snowflake worksheets and getting this error below. Tables are defined. Any feedbacks is appreciated.
Error:
Uncaught exception of type 'STATEMENT_ERROR' on line 9 at position 4 : SQL compilation error: error line 162 at position 34 invalid identifier 'CURRMONTH'
execute immediate $$
declare
firstmonth :=(select dateadd(month,case when day(current_timestamp())<=10 then -1 else 0 end,dateadd(month,0,date_trunc('month',current_date()))));
lastmonth :=(select dateadd(month,1,dateadd(month,case when day(current_timestamp())<=10 then -1 else 0 end,dateadd(month,0,date_trunc('month',current_date())))));
currmonth date default firstmonth;
begin
while (currmonth <=lastmonth) do
insert into temptable
select colA, colB, colC, colD
from tableA
where date between currmonth and dateadd(day,-1,dateadd(month,1,currmonth)) and date2 = currmonth
currmonth :=dateadd(month,1,currmonth)
end while;
end;
$$

try this, modified the column name from date to date1,
create or replace table temptable (date1 date,date2 date, colA varchar2, colB varchar2, colC varchar2, colD varchar2);
create or replace table tableA (date1 date,date2 date, colA varchar2, colB varchar2, colC varchar2, colD varchar2);
execute immediate $$
declare
firstmonth :=(select dateadd(month,case when day(current_timestamp())<=10 then -1 else 0 end,dateadd(month,0,date_trunc('month',current_date()))));
lastmonth :=(select dateadd(month,1,dateadd(month,case when day(current_timestamp())<=10 then -1 else 0 end,dateadd(month,0,date_trunc('month',current_date())))));
--currmonth date default firstmonth;
currmonth date ;
begin
currmonth :=firstmonth;
while (currmonth <=lastmonth) do
insert into temptable (colA, colB, colC, colD)
select colA, colB, colC, colD
from tableA
where date1 between :currmonth and dateadd(day,-1,dateadd(month,1,:currmonth)) and date2 = :currmonth;
currmonth :=dateadd(month,1,currmonth);
end while;
end;
$$
;

You are missing semi colon at end of statement.
currmonth :=dateadd(month,1,currmonth);

Related

SQL compilation error: error line 4 at position 24 invalid identifier Uncaught exception of type 'STATEMENT_ERROR' Invalid identifier

Getting Below error while running the procedure in snowflake
Uncaught exception of type 'STATEMENT_ERROR' on line 6 at position 0 : SQL compilation error: error line 4 at position 24 invalid identifier 'I.DEPTNO'
create table emp(empid number, ename varchar2(20), deptno number);
insert into emp values
(1,'John',10),
(2,'Alex',20),
(3,'Mick',20);
create table dept(deptno number, dname varchar2(20));
insert into dept values
(10,'Sales'),
(20,'HR');
create table emp2 as select * from emp limit 0;
create or replace procedure emp_dept_proc()
returns varchar(200)
language sql
as
$$
declare
c1 cursor for select empid,ename,deptno from emp where deptno=20;
begin
for i in c1 do
insert into emp2 (empid , ename ,deptno)
select empid,ename,deptno
from emp where deptno = i.deptno;
end for;
end;
$$
call emp_dept_proc();
So not super happy with the work around but using EXECUTE IMMEDIATE we can build the SQL we want to use:
create or replace procedure emp_dept_proc()
returns varchar(200)
language sql
as
$$
declare
c1 cursor for select empid,ename,deptno from emp where deptno=20;
begin
for i in c1 do
execute immediate 'insert into emp2 (empid , ename ,deptno)
select empid,ename,deptno
from emp where deptno = ' || i.deptno;
end for;
end;
$$;
truncate emp2;
call emp_dept_proc();
select * from emp2;
EMPID
ENAME
DEPTNO
2
Alex
20
3
Mick
20
2
Alex
20
3
Mick
20
you are getting 4 rows because you are inserting "all match departments to 20" x 2
The build the dynamic SQL version:
only slightly different but if you have complex SQL to build, this is more or less how I would do it:
create or replace procedure emp_dept_proc()
returns varchar(200)
language sql
as
$$
declare
c1 cursor for select empid,ename,deptno from emp where deptno = 20;
begin
for i in c1 do
let _sql varchar := 'insert into emp2 (empid , ename ,deptno) select empid, ename, deptno from emp where deptno = ' || i.deptno;
execute immediate _sql;
end for;
end;
$$;
The create new variables version:
so it works if your are using single named variables:
create or replace procedure emp_dept_proc()
returns varchar
language sql
as
$$
declare
c1 cursor for select empid, ename, deptno from emp where deptno = 20;
deptno number;
begin
for i in c1 do
deptno := i.deptno;
insert into emp2 (empid, ename, deptno)
select empid, ename, deptno
from emp
where deptno = :deptno;
end for;
end;
$$;

Dynamic where clause sql loop

Following is the stored procedure
ALTER PROCEDURE [dbo].[get_data_Dyna]
{
#param1 varchar(max) = null,
#param2 varchar(max) = null,
#start varchar(max) = null,
#end varchar(max) = null
}
AS
SELECT * from table where
(#param1 IS NULL OR column1 IN (SELECT data FROM dbo.delimited_list_to_table(#param1,',')))
AND (#param2 IS NULL OR column2 IN (SELECT data FROM dbo.delimited_list_to_table(#param2,',')))
AND ....?????
How this is working :
All the parameters can be comma seperated
#param1 value can be 'Germany' or 'Germany,USA' or null. This is working as expected.
Same goes for #param2
I'm trying to include rest of the parameters which is expected to work as follows :
#start='0' and #end='100' : In this case, where clause will look like this
...AND val BETWEEN #start AND #end
#start='48,60' and #end='51,99' : In this case, where clause will look like this
...AND ((val Between 48 and 51) or (val Between 60 and 99))
#start='48,60,75' and #end='51,99,203' : In this case, where clause will look like this
...AND ((val Between 48 and 51) or (val Between 60 and 99) or (val Between 75 and 203))
I'm unable to include above 2nd/3rd point correctly. I tried to write it dynamically which is working for single values [Point 1], but how to write point 2/3 ?
Any help is greatly appreciated.
Ok, i think the best approach here would be to use temp tables or table variable.
Lets go with temp tables.
create table #StartEnd (start int not null, end int not null, primary key (start,end))
then we insert from #start and #end into it using dbo.delimited_list_to_table. Now i am not sure about your implementation of it, so i will assume the values are numbered
insert into #StartEnd
select starts.data, ends.data
from dbo.delimited_list_to_table(#start,',') as starts
join dbo.delimited_list_to_table(#end,',') as ends
on starts.index = ends.index
Now we have to filter the values. Two approaches. Join or Exists condition
...
join #StartEnd on val between start and end
...
and exists (select 1 from #StartEnd where val between start and end)
Hope this helps
there you go. The comments / explainations are within the query
-- create a sample table
declare #tbl table
(
val int
)
-- put in some sample data
insert into #tbl
values (48), (60), (51), (99), (75), (203)
-- these are the input parameter
declare #start varchar(100),
#end varchar(100)
-- and these are the input value
select #start = '48,60,75',
#end = '51,99,203'
-- the actual query
; with
start_end as
(
-- here i am using [DelimitedSplit8K][1]
select s = s.Item, e = e.Item
from dbo.[DelimitedSplit8K](#start, ',') s
inner join dbo.[DelimitedSplit8K](#end, ',') e
on s.ItemNumber = e.ItemNumber
)
select t.val
from #tbl t
where exists
(
select *
from start_end x
where t.val between x.s and x.e
)
you can get it here DelimitedSplit8K
Sample input (from our understanding, we guess the your data):
select
* into ##delimit
from (
values
(1 ,'Ger','Ind', 100 )
,(2 ,'Ind',Null, 10 )
,(3 ,'Ger',Null, 24 )
,(4 ,'Ind','Ger', 54 )
,(5 ,'USA','Ind', 56 )
,(6 ,Null,'USA', 75 )-- NULL. But USA is three time came.
,(7 ,'USA','USA', 60 )-- same country with diff val.
,(8 ,'USA','USA', 80 )-- same country with diff val.
) demilit(Id,FromPr,ToPr,Val)
select * from ##delimit
Procedure (you just use this instead of your procedure):
CREATE PROCEDURE [dbo].[get_data_Dyna]
(#param1 varchar(max) = NULL,
#param2 varchar(max) = NULL,
#start varchar(max) = NULL,
#end varchar(max) = NULL)
AS
BEGIN
SELECT *
FROM ##delimit d
JOIN
( --| Here we check the val btw #start and #end
SELECT DISTINCT
s.FinalColumn StartVal, --|
e.FinalColumn EndVal --|
FROM
dbo.WithoutDelimit (#start, ',') s --| S means 'Start'
JOIN
(SELECT *
FROM dbo.WithoutDelimit (#end, ',')) e ON s.id = e.id --| E means 'End'
) se --| se mean StartEnd
ON d.val BETWEEN se.StartVal AND se.EndVal --| Here YOUR CONDITION is accomplished
WHERE
( -- | checks whether
frompr IN -- | column1 in #param1 or not
(SELECT FinalColumn FROM dbo.WithoutDelimit (#param1,',') -- | frompr means, 'column1'
) OR #param1 is NULL -- |
)
and ( -- | checks whether
ToPr in ( -- | column2 in #param2 or not
select FinalColumn from dbo.WithoutDelimit (#param2,',') -- | frompr means, 'column2'
) or #param2 is null -- |
)
end
Call stored procedure:
[get_data_Dyna] null,'usa','75','100,' -- 6 rows
[get_data_Dyna] 'Ind,Ger',null,'1,15','20,30' --2 and 3 rows are selected.
[get_data_Dyna] 'usa','usa','50,60','55,79'
-- 7 and 8 has same country. But due to Val, 8 has been rejected.
[get_data_Dyna] NULL,'usa','70,60','80,79'
-- 6 and 7 and 8 has been selected. Due to val condition.
Function (called from the stored procedure):
alter function WithoutDelimit -- We use one function for all conditions.
(#Parameter varchar (max),
#delimit varchar (1))
returns #FinalTable table (
Id int identity (1,1) -- Auto increment
, FinalColumn varchar (max) -- It returns the values as a column.
) as
begin
;with cte as -- recursive cte.
(
select convert (varchar (255), #Parameter + #delimit) con
, convert (varchar (255), #Parameter + #delimit) want
union all
select convert (varchar (255), stuff (con, 1, CHARINDEX (#demilit,con),'') )
, substring (con, 1, CHARINDEX (#delimit, con) - 1)
from cte
where con <> ''
) insert into #FinalTable (FinalColumn)
select want from cte
where con <> want
return
end
Revert us, if query need update.

Pivot table between two dates using SQL Server 2008 R2

I have the following table:
Example:
create table test
(
col_dt1 date,
col_dt2 date
)
Inserting some records:
insert into test values('2014-11-07','2014-12-01');
select * from test;
col_dt1 col_dt2
----------------------
2014-11-07 2014-12-01
Expected Result:
col_dt1 col_dt2 07 November 2014 08 November 2014 .................... 01 December 2014
-----------------------------------------------------------------------------------------------
2014-11-07 2014-12-01 1 0 .................... 1
I got stuck to get all dates between two dates to have the stuff column in pivot table.
DECLARE #Start_Date DATE, #End_Date DATE, #QUERY NVARCHAR(MAX), #SUBQUERY NVARCHAR(MAX), #ROWCOUNT int
CREATE TABLE #TempTable(
ID int IDENTITY(1,1) NOT NULL,
Start_Date date,
End_Date date,
Dates NVARCHAR(50),
HasDate int
)
CREATE TABLE #TempTable2(
Start_Date date,
End_Date date,
Dates NVARCHAR(50),
HasDate int
)
SET #Start_Date = (SELECT TOP 1 col_dt1 from test)
SET #End_Date = (SELECT TOP 1 col_dt2 from test)
INSERT INTO #TempTable
SELECT
#Start_Date as Start_Date,
#End_Date as End_Date,
RIGHT(REPLICATE('0', DAY(DATEADD(DAY,number,#Start_Date))) + CAST(DAY(DATEADD(DAY,number,#Start_Date)) AS NVARCHAR(2)), 2)
+' '+DATENAME(MONTH,DATEADD(DAY,number,#Start_Date))+' '+CONVERT(NVARCHAR(50),YEAR(DATEADD(DAY,number,#Start_Date))) as Start_Date,
HasDate =
CASE
WHEN DATEADD(DAY,number,#Start_Date)=#Start_Date THEN 1
WHEN DATEADD(DAY,number,#Start_Date)=#End_Date THEN 1
ELSE 0
END
FROM master..spt_values
WHERE type = 'P'
AND DATEADD(DAY,number,#Start_Date) <= #End_Date
INSERT INTO #TempTable2 SELECT [Start_Date],[End_Date],[Dates],HasDate FROM #TempTable
SELECT * FROM #TempTable
SET #QUERY=''
SET #QUERY+='SELECT * FROM #TempTable2
PIVOT
(
Max(HasDate)
FOR Dates IN ('
SET #SUBQUERY=''
SET #ROWCOUNT=1
WHILE #ROWCOUNT <= (SELECT COUNT(*) FROM #TempTable)
BEGIN
SET #SUBQUERY=#SUBQUERY+'['+(SELECT CONVERT(NVARCHAR(50),Dates)as Dates FROM #TempTable WHERE ID=#ROWCOUNT)+']'
IF (#ROWCOUNT<>(SELECT COUNT(*) FROM #TempTable))
BEGIN
SET #SUBQUERY=#SUBQUERY+','
END
SET #ROWCOUNT=#ROWCOUNT+1
END
SET #QUERY=#QUERY+#SUBQUERY+')
)AS tblPivot'
PRINT(#QUERY)
EXECUTE(#QUERY)
DROP TABLE #TempTable
DROP TABLE #TempTable2
You can try this.

IsNumeric Time value problem

Table1
Time
10:00:00
12:00:00
Absent
14:00:00
Holiday
...,
Time column Datatype is varchar
I want to check the time column value is numeric then 'Present'
Tried Query
Select case when isnumeric(time) then 'Present' else time end as time from table1
Showing error in 'then'
How to modify my query according to my requirement
Need Query Help
Try using ISDATE
Returns 1 if the expression is a valid
date, time, or datetime value;
otherwise, 0.
Something like
DECLARE #Table TABLE(
[Time] VARCHAR(50)
)
INSERT INTO #Table SELECT '10:00:00'
INSERT INTO #Table SELECT '12:00:00'
INSERT INTO #Table SELECT 'Absent'
INSERT INTO #Table SELECT '14:00:00'
INSERT INTO #Table SELECT 'Holiday'
SELECT *,
ISDATE(Time),
case when ISDATE(time) != 0 then 'Present' else time end
FROM #Table

Query to show result in the charts

I have to implement charts in my application. Suppose i have a table structure
DECLARE #SONGS TABLE
(
[ID] INT IDENTITY,
[SONGNAME] VARCHAR(20),
[CREATEDDATE] DATETIME
)
INSERT INTO #SONGS
SELECT 'SONG1','20091102' UNION ALL
SELECT 'SONG2','20091103' UNION ALL
SELECT 'SONG3','20091107' UNION ALL
SELECT 'SONG4','20091107' UNION ALL
SELECT 'SONG5','20091107' UNION ALL
SELECT 'SONG6','20091109'
Now user will pass start date and end date from outside as parameters like below
DECLARE #STARTDATE DATETIME
DECLARE #ENDDATE DATETIME
SET #STARTDATE='20091101'
SET #ENDDATE='20091111'
Now user has further one more option(SAY #OPTION VARCHAR(20) ) whether he wants the results with dates split into individual dates between the start date and end date, second option he can choose to have the results with dates into the months between the start date and end date, similarly for year.
--OUTPUT I NEED IS when #OPTION IS DATE
DATE [SONGCOUNT]
------------------------------------------
20091101 0
20091102 1
20091103 1
20091104 0
20091105 0
20091106 0
20091107 3
20091108 0
20091109 1
20091110 0
20091111 0
Similarly i want the results with dates splitted according the option(day,week,month,year) having count next to it.
My goal is to display date on xaxis and count on y axis, can you suggest me a way to implement the same.
DECLARE #dimDate TABLE (
myDate datetime
,dt int
,yr int
,ym int
)
DECLARE #dte datetime
SET #dte = #STARTDATE
WHILE #dte <= #ENDDATE
BEGIN
INSERT INTO #dimDate (myDate, dt, yr, ym)
VALUES(
#dte
,datepart(yy,#dte)*10000+ datepart(mm,#dte)*100 + datepart(dd,#dte)
,datepart(yy,#dte)
,datepart(yy,#dte)*100+ datepart(mm,#dte)
)
SET #dte = dateadd(dd,1,#dte)
END
.
DECLARE #option varchar(2)
SET #option ='dt'
.
-- per day
IF #option ='dt'
BEGIN
SELECT d.dt, COUNT(s.ID) AS "song_count"
FROM #dimDate AS d
LEFT JOIN #SONGS AS s ON d.myDate = s.CREATEDDATE
GROUP BY d.dt
END
.
-- per year
IF #option ='yr'
BEGIN
SELECT d.yr, COUNT(s.ID) AS "song_count"
FROM #dimDate AS d
LEFT JOIN #SONGS AS s ON d.myDate = s.CREATEDDATE
GROUP BY d.yr
END
.
-- per year-month
IF #option ='ym'
BEGIN
SELECT d.ym, COUNT(s.ID) AS "song_count"
FROM #dimDate AS d
LEFT JOIN #SONGS AS s ON d.myDate = s.CREATEDDATE
GROUP BY d.ym
END
For making the results in x & y axis, use PIVOT(SQL server 2005+).
This kind of queries are called CROSS TAB QUERIES
For your reference
SQL Server PIVOT examples

Resources