Home > Software design >  While loop: syntax error line 23 at position 10 unexpected '('. syntax error line 23 at po
While loop: syntax error line 23 at position 10 unexpected '('. syntax error line 23 at po

Time:11-06

I create a while loop to generate values and insert them into the existing table "PLAYGROUND"."BF_DEV".A5_DIMDATE_HUIQIONGWU

But there always is syntax error:

SQL compilation error: syntax error line 23 at position 10 unexpected '('. syntax error line 23 at position 20 unexpected '<'.

execute immediate $$
declare
    crrday date default '2005-01-01';
    datekey integer default 0;
    daynumberofweek integer default 0;
    endaynumberofweek varchar;
    daynumberofmonth integer default 0;
    daynumberofyear integer default 0;
    weeknumberofyear integer default 0;
    enmonth varchar;
    monthnumberofyear integer default 0;
    calquarter integer default 0;
    calyear integer default 0;
    calses integer default 0;
    fisyear integer default 0;
    fisquarter integer default 0;
    fisses integer default 0;
begin 
    let crrday date := '2005-01-01';
    let lastday date := '2031-01-01';
    let crryear integer := year(crrday);
    let lastyear integer := year(lastday);
    while (:crryear < lastyear) then
        datekey := convert(integer, crrday, 112);
        daynumberofweek := DAYOFWEEK(crrday);
        endaynumberofweek := decode(extract ('dayofweek_iso',crrday);
        daynumberofmonth := DAYOFMONTH(crrday);
        daynumberofyear := DAYOFYEAR(crrday);
        weeknumberofyear := WEEKOFYEAR(crrday);
        enmonth := decode(monthname(crrday));
        monthnumberofyear := MONTH(crrday);
        calquarter := QUARTER(crrday);
        calyear := year(crrday);
        calses := case when month(crrday) between 1 and 6 then 1 else 2 end;
        fisyear := case when month(crrday) between 1 and 6 then year(crrday) else dateadd(year, 1, crrday) end;
        fisquarter := case when month(crrday) between 7 and 9 then 1
                 case when month(crrday) between 10 and 12 then 2
                 case when month(crrday) between 1 and 3 then 3
                 else 4 end;
        fisses := case when month(crrday) between 7 and 12 then 1 else 2 end;
        insert into "PLAYGROUND"."BF_DEV".A5_DIMDATE_HUIQIONGWU values 
        (datekey, crrday, daynumberofweek, endaynumberofweek, daynumberofmonth, endaynumberofweek, daynumberofmonth, 
        daynumberofyear, weeknumberofyear, enmonth, monthnumberofyear, calquarter, calyear, calses, fisquarter, fisyear, fisses);
        crrday := dateadd(day, 1, crrday);
        crryear := year(crryear);
    end while;
end;
$$
;

CodePudding user response:

Have you considered using the GENERATOR function instead of a while loop? The code below is an example of that. (It is not in sproc format but should work as an example.)

  • Step 1: CTE with Generator function creates a range of dates.
  • Step 2: Select expressions to create the desired date transforms
/*********************************************************************************************
A WEEK_START session variable of 0 is the default Snowflake behavior and has weeks start on
Monday and end of Sunday (ISO standard).
Wherever possible, this script uses the ISO standard for WEEKS and DAY_OF_WEEK.
The DATEDIFF function does not support WEEKISO and therefore the WEEK_START parameter is set 
for the session in case this session parameter was set differently on the account or user. 
https://docs.snowflake.com/en/sql-reference/parameters.html#label-week-start
*********************************************************************************************/
alter session set week_start = 0;

/*********************************************************************************************
The parameters below define the temporal boundaries of the calendar table. The values must be 
DATE type and can be hardcoded, the result of a query, or a combination of both.
For example, you could set date_start and date_end based on the MIN and MAX date of the table
with the finest date granularity in your data.
*********************************************************************************************/

SET date_start = TO_DATE('2018-12-18');
SET date_end = current_date(); --TIP: for the current date use current_date();

--This sets the num_days parameter to the number of days between start and end
--this value is used for the generator
set num_days = (select datediff(day, $date_start, $date_end 1));

--CTE to hold generated date range
create or replace transient table calendar as 
with d as (
select
  dateadd(day,'-' || row_number() over (order by null), 
          dateadd(day, ' 1', $date_end)
         ) as date_key
from table (generator(rowcount => ($num_days)))
order by 1)
-- calendar table expressions 
select
    date_key,
    year(date_key) - (year(date_key) % 10) as decade,
    year(date_key) as year_,
    (year(date_key)::string || '-Q' || quarter(date_key)::string)::varchar(7) as year_qtr,
    (year(date_key)::string || '-' || lpad(month(date_key)::string, 2 ,'0'))::varchar(7) as year_month,
    (yearofweekiso(date_key)::string || '-' || lpad(weekiso(date_key)::string, 2 ,'0'))::varchar(7) as year_week_iso, --*see comments
    quarter(date_key) as qtr_of_year,
    month(date_key) as month_num,
    monthname(date_key) as month_name,
    time_slice(date_key, 1, 'month', 'start') as month_start_date,
    last_day(date_key, 'month')  as month_end_date, 
    weekiso(date_key) as week_iso_num, --*see comments
    yearofweekiso(date_key) as week_iso_year, --*see comments
    'W'||lpad(weekiso(date_key)::string,2,0) as week_iso_string, 
    iff(dayofweekiso(date_key) = 1, date_key, previous_day(date_key, 'mo')) as week_start_mon, --week starts on mon, ends on sun
    iff(dayofweekiso(date_key) = 7, date_key, next_day(date_key, 'su')) as week_end_mon, --week starts on mon, ends on sun
    iff(dayofweekiso(date_key) = 7, date_key, previous_day(date_key, 'su')) as week_start_sun, --week starts on sun, ends on sat
    iff(dayofweekiso(date_key) = 6, date_key, next_day(date_key, 'sa')) as week_end_sun, --week starts on sun, ends on sat
    dayofyear(date_key) as day_of_year,
    dayofmonth(date_key) as day_of_month,
    dayofweekiso(date_key) as day_of_week_iso,
    dayname(date_key) as day_name, 
    ceil(dayofmonth(date_key) / 7) as day_instance_in_month, --used to identify 'floating' events such as "fourth thursday of november" 
    iff(dayofweekiso(date_key) between 6 and 7, 1, 0) flag_day_is_weekend,
    iff(dayofweekiso(date_key) between 6 and 7, 0, 1) flag_day_is_weekday,
    iff(year(current_date())= year(date_key) and dayofyear(date_key) <= dayofyear(current_date()), 1, 0) as flag_cytd, --current year to date, date falls within current year to day
    iff(year(current_date()) - 1 = year(date_key) and dayofyear(date_key) <= dayofyear(current_date()), 1, 0) as flag_lytd, --last year to date, date falls within current year to day
    iff(dayofyear(date_key) <= dayofyear(current_date()), 1, 0) as flag_ytd, --year to date, date falls within the same days in the year, no matter which year
    iff(add_months(time_slice(date_key, 1, 'month', 'start'),1) = time_slice(current_date(), 1, 'month', 'start'),1 , 0) as flag_last_month,
    datediff(year, date_key, current_date()) as age_years_ago,
    datediff(month,date_key, current_date()) as age_months_ago,
    datediff(week,date_key, current_date()) as age_weeks_ago,
    datediff(day,date_key, current_date()) as age_days_ago
FROM D;

Example query:

select * from calendar where flag_lytd = 1
order by date_key desc
limit 10;
DATE_KEY DECADE YEAR_ YEAR_QTR YEAR_MONTH YEAR_WEEK_ISO QTR_OF_YEAR MONTH_NUM MONTH_NAME MONTH_START_DATE MONTH_END_DATE WEEK_ISO_NUM WEEK_ISO_YEAR WEEK_ISO_STRING WEEK_START_MON WEEK_END_MON WEEK_START_SUN WEEK_END_SUN DAY_OF_YEAR DAY_OF_MONTH DAY_OF_WEEK_ISO DAY_NAME DAY_INSTANCE_IN_MONTH FLAG_DAY_IS_WEEKEND FLAG_DAY_IS_WEEKDAY FLAG_CYTD FLAG_LYTD FLAG_YTD FLAG_LAST_MONTH AGE_YEARS_AGO AGE_MONTHS_AGO AGE_WEEKS_AGO AGE_DAYS_AGO
2021-11-05 2020 2021 2021-Q4 2021-11 2021-44 4 11 Nov 2021-11-01 2021-11-30 44 2,021 W44 2021-11-01 2021-11-07 2021-10-31 2021-11-06 309 5 5 Fri 1 0 1 0 1 1 0 1 12 52 365
2021-11-04 2020 2021 2021-Q4 2021-11 2021-44 4 11 Nov 2021-11-01 2021-11-30 44 2,021 W44 2021-11-01 2021-11-07 2021-10-31 2021-11-06 308 4 4 Thu 1 0 1 0 1 1 0 1 12 52 366
2021-11-03 2020 2021 2021-Q4 2021-11 2021-44 4 11 Nov 2021-11-01 2021-11-30 44 2,021 W44 2021-11-01 2021-11-07 2021-10-31 2021-11-06 307 3 3 Wed 1 0 1 0 1 1 0 1 12 52 367
2021-11-02 2020 2021 2021-Q4 2021-11 2021-44 4 11 Nov 2021-11-01 2021-11-30 44 2,021 W44 2021-11-01 2021-11-07 2021-10-31 2021-11-06 306 2 2 Tue 1 0 1 0 1 1 0 1 12 52 368
2021-11-01 2020 2021 2021-Q4 2021-11 2021-44 4 11 Nov 2021-11-01 2021-11-30 44 2,021 W44 2021-11-01 2021-11-07 2021-10-31 2021-11-06 305 1 1 Mon 1 0 1 0 1 1 0 1 12 52 369
2021-10-31 2020 2021 2021-Q4 2021-10 2021-43 4 10 Oct 2021-10-01 2021-10-31 43 2,021 W43 2021-10-25 2021-10-31 2021-10-31 2021-11-06 304 31 7 Sun 5 1 0 0 1 1 0 1 13 53 370
2021-10-30 2020 2021 2021-Q4 2021-10 2021-43 4 10 Oct 2021-10-01 2021-10-31 43 2,021 W43 2021-10-25 2021-10-31 2021-10-24 2021-10-30 303 30 6 Sat 5 1 0 0 1 1 0 1 13 53 371
2021-10-29 2020 2021 2021-Q4 2021-10 2021-43 4 10 Oct 2021-10-01 2021-10-31 43 2,021 W43 2021-10-25 2021-10-31 2021-10-24 2021-10-30 302 29 5 Fri 5 0 1 0 1 1 0 1 13 53 372
2021-10-28 2020 2021 2021-Q4 2021-10 2021-43 4 10 Oct 2021-10-01 2021-10-31 43 2,021 W43 2021-10-25 2021-10-31 2021-10-24 2021-10-30 301 28 4 Thu 4 0 1 0 1 1 0 1 13 53 373
2021-10-27 2020 2021 2021-Q4 2021-10 2021-43 4 10 Oct 2021-10-01 2021-10-31 43 2,021 W43 2021-10-25 2021-10-31 2021-10-24 2021-10-30 300 27 3 Wed 4 0 1 0 1 1 0 1 13 53 374

CodePudding user response:

I would not use a stored procedure to insert rows one at a time. Inserting single rows to a micropartition based table can take a few hundred milliseconds each. Multiplied by thousands of rows this can be slow, and there are some other issues with it. Here is your stored procedure logic expressed as a SELECT statement. You can then just insert into the table from the select:

select       row_number() over (order by null) - 1              DATEKEY
            ,dateadd('DAY', DATEKEY, '2005-01-01'::date)::date  CRRDAY
            ,dayofweek(CRRDAY)                                  DAYNUMBEROFWEEK
            ,extract('dayofweek_iso',CRRDAY)                    ENDDAYNUMBEROFWEEK -- What do you want here?
            ,dayofmonth(CRRDAY)                                 DAYNUMBEROFMONTH
            ,dayofmonth(last_day(CRRDAY, 'WEEK'))               ENDDAYNUMBEROFWEEK
            ,dayofyear(CRRDAY)                                  DAYNUMBEROFYEAR
            ,weekofyear(CRRDAY)                                 WEEKNUMBEROFYEAR
            ,monthname(CRRDAY)                                  ENMONTH
            ,month(CRRDAY)                                      MONTHNUMBEROFYEAR
            ,quarter(CRRDAY)                                    CALQUARTER
            ,year(CRRDAY)                                       CALYEAR
            ,iff(month(crrday) <= 6, 1, 2)                      CALSES
            ,case 
                when month(crrday) between 7 and 9 then 1
                when month(crrday) between 10 and 12 then 2
                when month(crrday) between 1 and 3 then 3
                else 4
             end                                                FISQUARTER
            ,iff(month(crrday) > 6, 1, 0)   year(crrday)        FISYEAR
            ,iff(month(CRRDAY) > 6, 1, 2)                       FISSES
            
from table(generator(rowcount => 10000))
qualify CRRDAY < '2031-01-01'::date 
order by CRRDAY;

First ten rows:

DATEKEY CRRDAY DAYNUMBEROFWEEK ENDDAYNUMBEROFWEEK DAYNUMBEROFMONTH ENDDAYNUMBEROFWEEK_1 DAYNUMBEROFYEAR WEEKNUMBEROFYEAR ENMONTH MONTHNUMBEROFYEAR CALQUARTER CALYEAR CALSES FISQUARTER FISYEAR FISSES
0 2005-01-01 6 6 1 2 1 53 Jan 1 1 2005 1 3 2005 2
1 2005-01-02 0 7 2 2 2 53 Jan 1 1 2005 1 3 2005 2
2 2005-01-03 1 1 3 9 3 1 Jan 1 1 2005 1 3 2005 2
3 2005-01-04 2 2 4 9 4 1 Jan 1 1 2005 1 3 2005 2
4 2005-01-05 3 3 5 9 5 1 Jan 1 1 2005 1 3 2005 2
5 2005-01-06 4 4 6 9 6 1 Jan 1 1 2005 1 3 2005 2
6 2005-01-07 5 5 7 9 7 1 Jan 1 1 2005 1 3 2005 2
7 2005-01-08 6 6 8 9 8 1 Jan 1 1 2005 1 3 2005 2
8 2005-01-09 0 7 9 9 9 1 Jan 1 1 2005 1 3 2005 2
9 2005-01-10 1 1 10 16 10 2 Jan 1 1 2005 1 3 2005 2
  • Related