Question

Is there some kind of middle ground between VARIANT and NOT VARIANT for Informix 10.0 SPL functions?

I'm kind of new to Informix. I've got a SPL function get_timezone_offset in Informix 10.0 that calculates whether a given timezone is in Daylight Savings Time or not and the resulting UTC offset (code posted below).

When using this function in the WHERE clause of a large table (>1M records) against a timestamp field it takes a few minutes to complete:

select count(*)
from Table
where Table.startDateTime >= current - (get_timezone_offset('et') units hour) - 1 units day

The same query without that function call is <1 second long:

select count(*)
from Table
where Table.startDateTime >= current - 1 units day

I believe the difference comes from the query optimizer being able to "simplify" current - 1 units day into a constant; I can modify the get_timezone_offset function with alter function get_timezone_offset with (add not variant) and then the first query including the function call takes less than a second to complete. But my function is in fact going to change output twice a year for the same input, so it's not not variant.

How can I get the optimizer to treat my function like a constant only for the duration of a query?

create procedure get_timezone_offset(timezone varchar(8))
    returning decimal as "current timezone offset";
-- This SQL calculates the current timezone's offset from UTC based on whether
-- the given timezone is in Daylight Saving Time right now or not.
-- DST is assumed to begin at 2:00 AM (in the given timezone) on the second
-- Sunday of March, and to end at 2:00 AM (in the given timezone, accounting for
-- DST) on the first Sunday of November.
-- Specify the timezone by giving a string for timezone which will correspond
-- to what's in the timezone_offsets table.

define offset decimal;

select
    "current timezone offset"
into offset
from (
    select
        "time (utc)",
        "in daylight savings time",
        "standard timezone offset",
        -- When we're in DST then the timezone offset from UTC increases by one
        case
            when "in daylight savings time" then "standard timezone offset" + 1
            else "standard timezone offset"
        end as "current timezone offset"
    from (
        select
            "time (utc)",
            -- Knowing when we currently are in relation to when the DST
            -- "triggers" are makes it easy to determine if we're in DST or not
            case
                when "time (utc)" >= "march trigger (utc)" and "time (utc)" < "november trigger (utc)" then 't'::boolean
                else 'f'::boolean
            end as "in daylight savings time",
            "standard timezone offset"
        from (
            select
                "time (utc)",
                -- DST begins at 2:00AM on the second Sunday in March. For
                -- Eastern Time, for example, that will be at 7:00 AM UTC
                mdy(3, "second sunday in march", year("time (utc)"))::datetime year to hour + ((2 - "standard timezone offset") units hour) as "march trigger (utc)",
                -- DST ends at 2:00AM on the first Sunday in November. For
                -- Eastern Time, for example, that will be at 6:00 AM UTC
                mdy(11, "first sunday in november", year("time (utc)"))::datetime year to hour + ((1 - "standard timezone offset") units hour) as "november trigger (utc)",
                "standard timezone offset"
            from (
                select
                    "time (utc)",
                    "standard timezone offset",
                    -- Figure out when the second Sunday in March and the first
                    -- Sunday in November are in UTC time
                    mod((7 - weekday(mdy(3, 1, year("time (utc)")))), 7) + 8 as "second sunday in march",
                    mod((7 - weekday(mdy(11, 1, year("time (utc)")))), 7) + 1 as "first sunday in november"
                from (
                    -- Get the timezone's UTC offset for when the timezone is in
                    -- standard time. Also get the current UTC time for later
                    -- calculations
                    select
                        "standard_utc_offset" as "standard timezone offset",
                        ('1970-01-01 00:00:00'::datetime year to second) + ((dbinfo('utc_current') / (60)) units minute) as "time (utc)"
                    from timezone_offsets
                    where timezone_offsets.timezone = timezone
                )
            )
        )
    )
);

return offset;

end procedure
Was it helpful?

Solution

Non variant means for a given input the output will always be the same no matter what. This allows engine magic. You are probably better off using two functions, one that returns the timezone info and one that returns the daylight savings adjustment - both of those are non-variant.

Your function only applies to the US AFAIC, the ROW uses week 13 and week 43 for the change over

OTHER TIPS

Well... formally you're right... the function is VARIANT. But I think you answered the question... You're asking how the engine can consider it to be NOT VARIANT for the duration of the query... If you set it to NOT VARIANT you do exactly what you say. And it would only return a wrong result if the query started before the DST change and ended after the DST change. For a query that runs for less than 1s and an event that happen onloy two times per year, I'd say you're pretty safe, but that's something you need to define.

Apart from these considerations everything you say is true. This is the difference between calling the function 1 time or 1M times... And calling SPL is slow (comparing to other options like C functions).

Regards.

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top