Trying to speed up this 'GetWorkingDays' function.

  • Jason A. Long - Friday, October 20, 2017 12:37 PM

    I don't think there's any reason to keep sitting on what I have as of right now. I haven't had a chance to plug Loius's weekend calculation yet. But it will happen today. I'm anxious to see if it makes a meaningful impact.
    It also needs inline comments... I'll get those added today as well...

    CREATE FUNCTION dbo.tfn_GetWorkingDays_X2
    /* =============================================================================
    10/18/2017 JL, Created: Completly in memory does not need the WorkingDaysPreCalc    
    ============================================================================= */
    (
        @BegDate DATETIME,
        @EndDate DATETIME
    )
    RETURNS TABLE WITH SCHEMABINDING AS
    RETURN

        WITH
            cte_weekend_count AS (
                SELECT
                    weekend_days = CASE
                        WHEN dp.beg_daywk = 1 AND dp.end_daywk = 7 THEN (dp.weeks_diff * 2) + 1
                        WHEN dp.beg_daywk = 7 AND dp.end_daywk = 7 THEN (dp.weeks_diff * 2)
                        WHEN dp.beg_daywk = 7 THEN (dp.weeks_diff * 2) - 1
                        WHEN dp.end_daywk = 7 THEN (dp.weeks_diff * 2) + 1
                        ELSE dp.weeks_diff * 2
                    END
                    FROM
                ( VALUES
                    (    DATEDIFF(WEEK, DATEADD(DAY, DATEDIFF(DAY, 0, @BegDate), 0),
                        DATEADD(DAY, DATEDIFF(DAY, 0, @EndDate), 0)),
                        DATEPART(dw, @BegDate),
                        DATEPART(dw, @EndDate)
                    )
                    ) dp ( weeks_diff, beg_daywk, end_daywk )
                ),
            cte_holiday (f, l) AS (
                SELECT
                    CASE
                        WHEN '2000-05-29' >= @BegDate THEN 0 WHEN '2000-07-04' >= @BegDate THEN 1 WHEN '2000-09-04' >= @BegDate THEN 2 WHEN '2000-11-23' >= @BegDate THEN 3 WHEN '2000-11-24' >= @BegDate THEN 4 WHEN '2000-12-25' >= @BegDate THEN 5
                        WHEN '2001-01-01' >= @BegDate THEN 6 WHEN '2001-05-28' >= @BegDate THEN 7 WHEN '2001-07-04' >= @BegDate THEN 8 WHEN '2001-09-03' >= @BegDate THEN 9 WHEN '2001-11-22' >= @BegDate THEN 10 WHEN '2001-11-23' >= @BegDate THEN 11
                        WHEN '2001-12-25' >= @BegDate THEN 12 WHEN '2002-01-01' >= @BegDate THEN 13 WHEN '2002-05-27' >= @BegDate THEN 14 WHEN '2002-07-04' >= @BegDate THEN 15 WHEN '2002-09-02' >= @BegDate THEN 16 WHEN '2002-11-28' >= @BegDate THEN 17
                        WHEN '2002-11-29' >= @BegDate THEN 18 WHEN '2002-12-25' >= @BegDate THEN 19 WHEN '2003-01-01' >= @BegDate THEN 20 WHEN '2003-05-26' >= @BegDate THEN 21 WHEN '2003-07-04' >= @BegDate THEN 22 WHEN '2003-09-01' >= @BegDate THEN 23
                        WHEN '2003-11-27' >= @BegDate THEN 24 WHEN '2003-11-28' >= @BegDate THEN 25 WHEN '2003-12-25' >= @BegDate THEN 26 WHEN '2004-01-01' >= @BegDate THEN 27 WHEN '2004-05-31' >= @BegDate THEN 28 WHEN '2004-07-05' >= @BegDate THEN 29
                        WHEN '2004-09-06' >= @BegDate THEN 30 WHEN '2004-11-25' >= @BegDate THEN 31 WHEN '2004-11-26' >= @BegDate THEN 32 WHEN '2004-12-24' >= @BegDate THEN 33 WHEN '2004-12-31' >= @BegDate THEN 34 WHEN '2005-05-30' >= @BegDate THEN 35
                        WHEN '2005-07-04' >= @BegDate THEN 36 WHEN '2005-09-05' >= @BegDate THEN 37 WHEN '2005-11-24' >= @BegDate THEN 38 WHEN '2005-11-25' >= @BegDate THEN 39 WHEN '2005-12-26' >= @BegDate THEN 40 WHEN '2006-01-02' >= @BegDate THEN 41
                        WHEN '2006-05-29' >= @BegDate THEN 42 WHEN '2006-07-04' >= @BegDate THEN 43 WHEN '2006-09-04' >= @BegDate THEN 44 WHEN '2006-11-23' >= @BegDate THEN 45 WHEN '2006-11-24' >= @BegDate THEN 46 WHEN '2006-12-25' >= @BegDate THEN 47
                        WHEN '2007-01-01' >= @BegDate THEN 48 WHEN '2007-05-28' >= @BegDate THEN 49 WHEN '2007-07-04' >= @BegDate THEN 50 WHEN '2007-09-03' >= @BegDate THEN 51 WHEN '2007-11-22' >= @BegDate THEN 52 WHEN '2007-11-23' >= @BegDate THEN 53
                        WHEN '2007-12-25' >= @BegDate THEN 54 WHEN '2008-01-01' >= @BegDate THEN 55 WHEN '2008-05-26' >= @BegDate THEN 56 WHEN '2008-07-04' >= @BegDate THEN 57 WHEN '2008-09-01' >= @BegDate THEN 58 WHEN '2008-11-27' >= @BegDate THEN 59
                        WHEN '2008-11-28' >= @BegDate THEN 60 WHEN '2008-12-25' >= @BegDate THEN 61 WHEN '2009-01-01' >= @BegDate THEN 62 WHEN '2009-05-25' >= @BegDate THEN 63 WHEN '2009-07-03' >= @BegDate THEN 64 WHEN '2009-09-07' >= @BegDate THEN 65
                        WHEN '2009-11-26' >= @BegDate THEN 66 WHEN '2009-11-27' >= @BegDate THEN 67 WHEN '2009-12-25' >= @BegDate THEN 68 WHEN '2010-01-01' >= @BegDate THEN 69 WHEN '2010-05-31' >= @BegDate THEN 70 WHEN '2010-07-05' >= @BegDate THEN 71
                        WHEN '2010-09-06' >= @BegDate THEN 72 WHEN '2010-11-25' >= @BegDate THEN 73 WHEN '2010-11-26' >= @BegDate THEN 74 WHEN '2010-12-24' >= @BegDate THEN 75 WHEN '2010-12-31' >= @BegDate THEN 76 WHEN '2011-05-30' >= @BegDate THEN 77
                        WHEN '2011-07-04' >= @BegDate THEN 78 WHEN '2011-09-05' >= @BegDate THEN 79 WHEN '2011-11-24' >= @BegDate THEN 80 WHEN '2011-11-25' >= @BegDate THEN 81 WHEN '2011-12-26' >= @BegDate THEN 82 WHEN '2012-01-02' >= @BegDate THEN 83
                        WHEN '2012-05-28' >= @BegDate THEN 84 WHEN '2012-07-04' >= @BegDate THEN 85 WHEN '2012-09-03' >= @BegDate THEN 86 WHEN '2012-11-22' >= @BegDate THEN 87 WHEN '2012-11-23' >= @BegDate THEN 88 WHEN '2012-12-25' >= @BegDate THEN 89
                        WHEN '2013-01-01' >= @BegDate THEN 90 WHEN '2013-05-27' >= @BegDate THEN 91 WHEN '2013-07-04' >= @BegDate THEN 92 WHEN '2013-09-02' >= @BegDate THEN 93 WHEN '2013-11-28' >= @BegDate THEN 94 WHEN '2013-11-29' >= @BegDate THEN 95
                        WHEN '2013-12-25' >= @BegDate THEN 96 WHEN '2014-01-01' >= @BegDate THEN 97 WHEN '2014-05-26' >= @BegDate THEN 98 WHEN '2014-07-04' >= @BegDate THEN 99 WHEN '2014-09-01' >= @BegDate THEN 100 WHEN '2014-11-27' >= @BegDate THEN 101
                        WHEN '2014-11-28' >= @BegDate THEN 102 WHEN '2014-12-25' >= @BegDate THEN 103 WHEN '2015-01-01' >= @BegDate THEN 104 WHEN '2015-05-25' >= @BegDate THEN 105 WHEN '2015-07-03' >= @BegDate THEN 106 WHEN '2015-09-07' >= @BegDate THEN 107
                        WHEN '2015-11-26' >= @BegDate THEN 108 WHEN '2015-11-27' >= @BegDate THEN 109 WHEN '2015-12-25' >= @BegDate THEN 110 WHEN '2016-01-01' >= @BegDate THEN 111 WHEN '2016-05-30' >= @BegDate THEN 112 WHEN '2016-07-04' >= @BegDate THEN 113
                        WHEN '2016-09-05' >= @BegDate THEN 114 WHEN '2016-11-24' >= @BegDate THEN 115 WHEN '2016-11-25' >= @BegDate THEN 116 WHEN '2016-12-26' >= @BegDate THEN 117 WHEN '2017-01-02' >= @BegDate THEN 118 WHEN '2017-05-29' >= @BegDate THEN 119
                        WHEN '2017-07-04' >= @BegDate THEN 120 WHEN '2017-09-04' >= @BegDate THEN 121 WHEN '2017-11-23' >= @BegDate THEN 122 WHEN '2017-11-24' >= @BegDate THEN 123 WHEN '2017-12-25' >= @BegDate THEN 124 WHEN '2018-01-01' >= @BegDate THEN 125
                        WHEN '2018-05-28' >= @BegDate THEN 126 WHEN '2018-07-04' >= @BegDate THEN 127 WHEN '2018-09-03' >= @BegDate THEN 128 WHEN '2018-11-22' >= @BegDate THEN 129 WHEN '2018-11-23' >= @BegDate THEN 130 WHEN '2018-12-25' >= @BegDate THEN 131
                        WHEN '2019-01-01' >= @BegDate THEN 132 WHEN '2019-05-27' >= @BegDate THEN 133 WHEN '2019-07-04' >= @BegDate THEN 134 WHEN '2019-09-02' >= @BegDate THEN 135 WHEN '2019-11-28' >= @BegDate THEN 136 WHEN '2019-11-29' >= @BegDate THEN 137
                        WHEN '2019-12-25' >= @BegDate THEN 138 WHEN '2020-01-01' >= @BegDate THEN 139 WHEN '2020-05-25' >= @BegDate THEN 140 WHEN '2020-07-03' >= @BegDate THEN 141 WHEN '2020-09-07' >= @BegDate THEN 142 WHEN '2020-11-26' >= @BegDate THEN 143
                        WHEN '2020-11-27' >= @BegDate THEN 144 WHEN '2020-12-25' >= @BegDate THEN 145 WHEN '2021-01-01' >= @BegDate THEN 146 WHEN '2021-05-31' >= @BegDate THEN 147 WHEN '2021-07-05' >= @BegDate THEN 148 WHEN '2021-09-06' >= @BegDate THEN 149
                        WHEN '2021-11-25' >= @BegDate THEN 150 WHEN '2021-11-26' >= @BegDate THEN 151 WHEN '2021-12-24' >= @BegDate THEN 152 WHEN '2021-12-31' >= @BegDate THEN 153 WHEN '2022-05-30' >= @BegDate THEN 154 WHEN '2022-07-04' >= @BegDate THEN 155
                        WHEN '2022-09-05' >= @BegDate THEN 156 WHEN '2022-11-24' >= @BegDate THEN 157 WHEN '2022-11-25' >= @BegDate THEN 158 WHEN '2022-12-26' >= @BegDate THEN 159 WHEN '2023-01-02' >= @BegDate THEN 160 WHEN '2023-05-29' >= @BegDate THEN 161
                        WHEN '2023-07-04' >= @BegDate THEN 162 WHEN '2023-09-04' >= @BegDate THEN 163 WHEN '2023-11-23' >= @BegDate THEN 164 WHEN '2023-11-24' >= @BegDate THEN 165 WHEN '2023-12-25' >= @BegDate THEN 166 WHEN '2024-01-01' >= @BegDate THEN 167
                        WHEN '2024-05-27' >= @BegDate THEN 168 WHEN '2024-07-04' >= @BegDate THEN 169 WHEN '2024-09-02' >= @BegDate THEN 170 WHEN '2024-11-28' >= @BegDate THEN 171 WHEN '2024-11-29' >= @BegDate THEN 172 WHEN '2024-12-25' >= @BegDate THEN 173
                        WHEN '2025-01-01' >= @BegDate THEN 174 WHEN '2025-05-26' >= @BegDate THEN 175 WHEN '2025-07-04' >= @BegDate THEN 176 WHEN '2025-09-01' >= @BegDate THEN 177 WHEN '2025-11-27' >= @BegDate THEN 178 WHEN '2025-11-28' >= @BegDate THEN 179
                        WHEN '2025-12-25' >= @BegDate THEN 180 WHEN '2026-01-01' >= @BegDate THEN 181 WHEN '2026-05-25' >= @BegDate THEN 182 WHEN '2026-07-03' >= @BegDate THEN 183 WHEN '2026-09-07' >= @BegDate THEN 184 WHEN '2026-11-26' >= @BegDate THEN 185
                        WHEN '2026-11-27' >= @BegDate THEN 186 WHEN '2026-12-25' >= @BegDate THEN 187 WHEN '2027-01-01' >= @BegDate THEN 188 WHEN '2027-05-31' >= @BegDate THEN 189 WHEN '2027-07-05' >= @BegDate THEN 190 WHEN '2027-09-06' >= @BegDate THEN 191
                        WHEN '2027-11-25' >= @BegDate THEN 192 WHEN '2027-11-26' >= @BegDate THEN 193 WHEN '2027-12-24' >= @BegDate THEN 194 WHEN '2027-12-31' >= @BegDate THEN 195 WHEN '2028-05-29' >= @BegDate THEN 196 WHEN '2028-07-04' >= @BegDate THEN 197
                        WHEN '2028-09-04' >= @BegDate THEN 198 WHEN '2028-11-23' >= @BegDate THEN 199 WHEN '2028-11-24' >= @BegDate THEN 200 WHEN '2028-12-25' >= @BegDate THEN 201 WHEN '2029-01-01' >= @BegDate THEN 202 WHEN '2029-05-28' >= @BegDate THEN 203
                        WHEN '2029-07-04' >= @BegDate THEN 204 WHEN '2029-09-03' >= @BegDate THEN 205 WHEN '2029-11-22' >= @BegDate THEN 206 WHEN '2029-11-23' >= @BegDate THEN 207 WHEN '2029-12-25' >= @BegDate THEN 208
                    END,
                    CASE
                        WHEN '2000-05-29' >= @EndDate THEN 0 WHEN '2000-07-04' >= @EndDate THEN 1 WHEN '2000-09-04' >= @EndDate THEN 2 WHEN '2000-11-23' >= @EndDate THEN 3 WHEN '2000-11-24' >= @EndDate THEN 4 WHEN '2000-12-25' >= @EndDate THEN 5
                        WHEN '2001-01-01' >= @EndDate THEN 6 WHEN '2001-05-28' >= @EndDate THEN 7 WHEN '2001-07-04' >= @EndDate THEN 8 WHEN '2001-09-03' >= @EndDate THEN 9 WHEN '2001-11-22' >= @EndDate THEN 10 WHEN '2001-11-23' >= @EndDate THEN 11
                        WHEN '2001-12-25' >= @EndDate THEN 12 WHEN '2002-01-01' >= @EndDate THEN 13 WHEN '2002-05-27' >= @EndDate THEN 14 WHEN '2002-07-04' >= @EndDate THEN 15 WHEN '2002-09-02' >= @EndDate THEN 16 WHEN '2002-11-28' >= @EndDate THEN 17
                        WHEN '2002-11-29' >= @EndDate THEN 18 WHEN '2002-12-25' >= @EndDate THEN 19 WHEN '2003-01-01' >= @EndDate THEN 20 WHEN '2003-05-26' >= @EndDate THEN 21 WHEN '2003-07-04' >= @EndDate THEN 22 WHEN '2003-09-01' >= @EndDate THEN 23
                        WHEN '2003-11-27' >= @EndDate THEN 24 WHEN '2003-11-28' >= @EndDate THEN 25 WHEN '2003-12-25' >= @EndDate THEN 26 WHEN '2004-01-01' >= @EndDate THEN 27 WHEN '2004-05-31' >= @EndDate THEN 28 WHEN '2004-07-05' >= @EndDate THEN 29
                        WHEN '2004-09-06' >= @EndDate THEN 30 WHEN '2004-11-25' >= @EndDate THEN 31 WHEN '2004-11-26' >= @EndDate THEN 32 WHEN '2004-12-24' >= @EndDate THEN 33 WHEN '2004-12-31' >= @EndDate THEN 34 WHEN '2005-05-30' >= @EndDate THEN 35
                        WHEN '2005-07-04' >= @EndDate THEN 36 WHEN '2005-09-05' >= @EndDate THEN 37 WHEN '2005-11-24' >= @EndDate THEN 38 WHEN '2005-11-25' >= @EndDate THEN 39 WHEN '2005-12-26' >= @EndDate THEN 40 WHEN '2006-01-02' >= @EndDate THEN 41
                        WHEN '2006-05-29' >= @EndDate THEN 42 WHEN '2006-07-04' >= @EndDate THEN 43 WHEN '2006-09-04' >= @EndDate THEN 44 WHEN '2006-11-23' >= @EndDate THEN 45 WHEN '2006-11-24' >= @EndDate THEN 46 WHEN '2006-12-25' >= @EndDate THEN 47
                        WHEN '2007-01-01' >= @EndDate THEN 48 WHEN '2007-05-28' >= @EndDate THEN 49 WHEN '2007-07-04' >= @EndDate THEN 50 WHEN '2007-09-03' >= @EndDate THEN 51 WHEN '2007-11-22' >= @EndDate THEN 52 WHEN '2007-11-23' >= @EndDate THEN 53
                        WHEN '2007-12-25' >= @EndDate THEN 54 WHEN '2008-01-01' >= @EndDate THEN 55 WHEN '2008-05-26' >= @EndDate THEN 56 WHEN '2008-07-04' >= @EndDate THEN 57 WHEN '2008-09-01' >= @EndDate THEN 58 WHEN '2008-11-27' >= @EndDate THEN 59
                        WHEN '2008-11-28' >= @EndDate THEN 60 WHEN '2008-12-25' >= @EndDate THEN 61 WHEN '2009-01-01' >= @EndDate THEN 62 WHEN '2009-05-25' >= @EndDate THEN 63 WHEN '2009-07-03' >= @EndDate THEN 64 WHEN '2009-09-07' >= @EndDate THEN 65
                        WHEN '2009-11-26' >= @EndDate THEN 66 WHEN '2009-11-27' >= @EndDate THEN 67 WHEN '2009-12-25' >= @EndDate THEN 68 WHEN '2010-01-01' >= @EndDate THEN 69 WHEN '2010-05-31' >= @EndDate THEN 70 WHEN '2010-07-05' >= @EndDate THEN 71
                        WHEN '2010-09-06' >= @EndDate THEN 72 WHEN '2010-11-25' >= @EndDate THEN 73 WHEN '2010-11-26' >= @EndDate THEN 74 WHEN '2010-12-24' >= @EndDate THEN 75 WHEN '2010-12-31' >= @EndDate THEN 76 WHEN '2011-05-30' >= @EndDate THEN 77
                        WHEN '2011-07-04' >= @EndDate THEN 78 WHEN '2011-09-05' >= @EndDate THEN 79 WHEN '2011-11-24' >= @EndDate THEN 80 WHEN '2011-11-25' >= @EndDate THEN 81 WHEN '2011-12-26' >= @EndDate THEN 82 WHEN '2012-01-02' >= @EndDate THEN 83
                        WHEN '2012-05-28' >= @EndDate THEN 84 WHEN '2012-07-04' >= @EndDate THEN 85 WHEN '2012-09-03' >= @EndDate THEN 86 WHEN '2012-11-22' >= @EndDate THEN 87 WHEN '2012-11-23' >= @EndDate THEN 88 WHEN '2012-12-25' >= @EndDate THEN 89
                        WHEN '2013-01-01' >= @EndDate THEN 90 WHEN '2013-05-27' >= @EndDate THEN 91 WHEN '2013-07-04' >= @EndDate THEN 92 WHEN '2013-09-02' >= @EndDate THEN 93 WHEN '2013-11-28' >= @EndDate THEN 94 WHEN '2013-11-29' >= @EndDate THEN 95
                        WHEN '2013-12-25' >= @EndDate THEN 96 WHEN '2014-01-01' >= @EndDate THEN 97 WHEN '2014-05-26' >= @EndDate THEN 98 WHEN '2014-07-04' >= @EndDate THEN 99 WHEN '2014-09-01' >= @EndDate THEN 100 WHEN '2014-11-27' >= @EndDate THEN 101
                        WHEN '2014-11-28' >= @EndDate THEN 102 WHEN '2014-12-25' >= @EndDate THEN 103 WHEN '2015-01-01' >= @EndDate THEN 104 WHEN '2015-05-25' >= @EndDate THEN 105 WHEN '2015-07-03' >= @EndDate THEN 106 WHEN '2015-09-07' >= @EndDate THEN 107
                        WHEN '2015-11-26' >= @EndDate THEN 108 WHEN '2015-11-27' >= @EndDate THEN 109 WHEN '2015-12-25' >= @EndDate THEN 110 WHEN '2016-01-01' >= @EndDate THEN 111 WHEN '2016-05-30' >= @EndDate THEN 112 WHEN '2016-07-04' >= @EndDate THEN 113
                        WHEN '2016-09-05' >= @EndDate THEN 114 WHEN '2016-11-24' >= @EndDate THEN 115 WHEN '2016-11-25' >= @EndDate THEN 116 WHEN '2016-12-26' >= @EndDate THEN 117 WHEN '2017-01-02' >= @EndDate THEN 118 WHEN '2017-05-29' >= @EndDate THEN 119
                        WHEN '2017-07-04' >= @EndDate THEN 120 WHEN '2017-09-04' >= @EndDate THEN 121 WHEN '2017-11-23' >= @EndDate THEN 122 WHEN '2017-11-24' >= @EndDate THEN 123 WHEN '2017-12-25' >= @EndDate THEN 124 WHEN '2018-01-01' >= @EndDate THEN 125
                        WHEN '2018-05-28' >= @EndDate THEN 126 WHEN '2018-07-04' >= @EndDate THEN 127 WHEN '2018-09-03' >= @EndDate THEN 128 WHEN '2018-11-22' >= @EndDate THEN 129 WHEN '2018-11-23' >= @EndDate THEN 130 WHEN '2018-12-25' >= @EndDate THEN 131
                        WHEN '2019-01-01' >= @EndDate THEN 132 WHEN '2019-05-27' >= @EndDate THEN 133 WHEN '2019-07-04' >= @EndDate THEN 134 WHEN '2019-09-02' >= @EndDate THEN 135 WHEN '2019-11-28' >= @EndDate THEN 136 WHEN '2019-11-29' >= @EndDate THEN 137
                        WHEN '2019-12-25' >= @EndDate THEN 138 WHEN '2020-01-01' >= @EndDate THEN 139 WHEN '2020-05-25' >= @EndDate THEN 140 WHEN '2020-07-03' >= @EndDate THEN 141 WHEN '2020-09-07' >= @EndDate THEN 142 WHEN '2020-11-26' >= @EndDate THEN 143
                        WHEN '2020-11-27' >= @EndDate THEN 144 WHEN '2020-12-25' >= @EndDate THEN 145 WHEN '2021-01-01' >= @EndDate THEN 146 WHEN '2021-05-31' >= @EndDate THEN 147 WHEN '2021-07-05' >= @EndDate THEN 148 WHEN '2021-09-06' >= @EndDate THEN 149
                        WHEN '2021-11-25' >= @EndDate THEN 150 WHEN '2021-11-26' >= @EndDate THEN 151 WHEN '2021-12-24' >= @EndDate THEN 152 WHEN '2021-12-31' >= @EndDate THEN 153 WHEN '2022-05-30' >= @EndDate THEN 154 WHEN '2022-07-04' >= @EndDate THEN 155
                        WHEN '2022-09-05' >= @EndDate THEN 156 WHEN '2022-11-24' >= @EndDate THEN 157 WHEN '2022-11-25' >= @EndDate THEN 158 WHEN '2022-12-26' >= @EndDate THEN 159 WHEN '2023-01-02' >= @EndDate THEN 160 WHEN '2023-05-29' >= @EndDate THEN 161
                        WHEN '2023-07-04' >= @EndDate THEN 162 WHEN '2023-09-04' >= @EndDate THEN 163 WHEN '2023-11-23' >= @EndDate THEN 164 WHEN '2023-11-24' >= @EndDate THEN 165 WHEN '2023-12-25' >= @EndDate THEN 166 WHEN '2024-01-01' >= @EndDate THEN 167
                        WHEN '2024-05-27' >= @EndDate THEN 168 WHEN '2024-07-04' >= @EndDate THEN 169 WHEN '2024-09-02' >= @EndDate THEN 170 WHEN '2024-11-28' >= @EndDate THEN 171 WHEN '2024-11-29' >= @EndDate THEN 172 WHEN '2024-12-25' >= @EndDate THEN 173
                        WHEN '2025-01-01' >= @EndDate THEN 174 WHEN '2025-05-26' >= @EndDate THEN 175 WHEN '2025-07-04' >= @EndDate THEN 176 WHEN '2025-09-01' >= @EndDate THEN 177 WHEN '2025-11-27' >= @EndDate THEN 178 WHEN '2025-11-28' >= @EndDate THEN 179
                        WHEN '2025-12-25' >= @EndDate THEN 180 WHEN '2026-01-01' >= @EndDate THEN 181 WHEN '2026-05-25' >= @EndDate THEN 182 WHEN '2026-07-03' >= @EndDate THEN 183 WHEN '2026-09-07' >= @EndDate THEN 184 WHEN '2026-11-26' >= @EndDate THEN 185
                        WHEN '2026-11-27' >= @EndDate THEN 186 WHEN '2026-12-25' >= @EndDate THEN 187 WHEN '2027-01-01' >= @EndDate THEN 188 WHEN '2027-05-31' >= @EndDate THEN 189 WHEN '2027-07-05' >= @EndDate THEN 190 WHEN '2027-09-06' >= @EndDate THEN 191
                        WHEN '2027-11-25' >= @EndDate THEN 192 WHEN '2027-11-26' >= @EndDate THEN 193 WHEN '2027-12-24' >= @EndDate THEN 194 WHEN '2027-12-31' >= @EndDate THEN 195 WHEN '2028-05-29' >= @EndDate THEN 196 WHEN '2028-07-04' >= @EndDate THEN 197
                        WHEN '2028-09-04' >= @EndDate THEN 198 WHEN '2028-11-23' >= @EndDate THEN 199 WHEN '2028-11-24' >= @EndDate THEN 200 WHEN '2028-12-25' >= @EndDate THEN 201 WHEN '2029-01-01' >= @EndDate THEN 202 WHEN '2029-05-28' >= @EndDate THEN 203
                        WHEN '2029-07-04' >= @EndDate THEN 204 WHEN '2029-09-03' >= @EndDate THEN 205 WHEN '2029-11-22' >= @EndDate THEN 206 WHEN '2029-11-23' >= @EndDate THEN 207 WHEN '2029-12-25' >= @EndDate THEN 208
                    END
                )
        SELECT
            WorkingDays = DATEDIFF(DAY, DATEADD(DAY, DATEDIFF(DAY, 0, @BegDate), 0), DATEADD(DAY, DATEDIFF(DAY, 0, @EndDate), 0)) - (w.weekend_days + (h.l - h.f))
        FROM
            cte_weekend_count w
            JOIN cte_holiday h
                ON 1 = 1;
    GO

    IMHO this is a long way around the barn to get what you asked for.

    -------------------------------------------------------------
    we travel not to escape life but for life not to escape us
    Don't fear failure, fear regret.

  • below86 - Friday, October 20, 2017 1:44 PM

    IMHO this is a long way around the barn to get what you asked for.

    How so?

  • Jason A. Long - Friday, October 20, 2017 2:27 PM

    below86 - Friday, October 20, 2017 1:44 PM

    IMHO this is a long way around the barn to get what you asked for.

    How so?

    You want the number of working days between two dates, excluding weekends and holidays. Right?  You already know what days are weekends and what days are holidays.  Why make it calculate it each time you run the function?  What happens in your code if you have a new holiday to add?  Let's say you start recognizing presidents day as a holiday in 2018(02/19/2018).  You need to change both case statements by adding this date, then you need to increment all of your numbers in your THEN statement after this date.  Seems like a lot of work and a big chance for human error.  You create a table like I mentioned then you only have to update a few entries in the table.  Your function never has to be touched.  I know the chances of that happening are slim, again IMHO I think your code makes it harder than it needs to be.  I guess I couldn't stay out of it. 🙂

    -------------------------------------------------------------
    we travel not to escape life but for life not to escape us
    Don't fear failure, fear regret.

  • below86 - Friday, October 20, 2017 2:47 PM

    Jason A. Long - Friday, October 20, 2017 2:27 PM

    below86 - Friday, October 20, 2017 1:44 PM

    IMHO this is a long way around the barn to get what you asked for.

    How so?

    You want the number of working days between two dates, excluding weekends and holidays. Right?  You already know what days are weekends and what days are holidays.  Why make it calculate it each time you run the function?  What happens in your code if you have a new holiday to add?  Let's say you start recognizing presidents day as a holiday in 2018(02/19/2018).  You need to change both case statements by adding this date, then you need to increment all of your numbers in your THEN statement after this date.  Seems like a lot of work and a big chance for human error.  You create a table like I mentioned then you only have to update a few entries in the table.  Your function never has to be touched.  I know the chances of that happening are slim, again IMHO I think your code makes it harder than it needs to be.  I guess I couldn't stay out of it. 🙂

    That has been pretty much my thoughts about this.  I hate to have to change code to add even 1 new holiday for 1 year.

    I have an old trick with a Calendar table that I've not had time to test for this.  I'll give it a run this weekend and see.  I don't know if it'll be able to beat 3 seconds on a million rows but it'll make maintenance a breeze.  As for those that think holidays and workdays don't change much, you should work where I work. 😉

    --Jeff Moden


    RBAR is pronounced "ree-bar" and is a "Modenism" for Row-By-Agonizing-Row.
    First step towards the paradigm shift of writing Set Based code:
    ________Stop thinking about what you want to do to a ROW... think, instead, of what you want to do to a COLUMN.
    "Change is inevitable... change for the better is not".

    Helpful Links:
    How to post code problems
    How to Post Performance Problems
    Create a Tally Function (fnTally)
    Intro to Tally Tables and Functions

  • below86 - Friday, October 20, 2017 2:47 PM

    Jason A. Long - Friday, October 20, 2017 2:27 PM

    below86 - Friday, October 20, 2017 1:44 PM

    IMHO this is a long way around the barn to get what you asked for.

    How so?

    You want the number of working days between two dates, excluding weekends and holidays. Right?  You already know what days are weekends and what days are holidays.  Why make it calculate it each time you run the function?  What happens in your code if you have a new holiday to add?  Let's say you start recognizing presidents day as a holiday in 2018(02/19/2018).  You need to change both case statements by adding this date, then you need to increment all of your numbers in your THEN statement after this date.  Seems like a lot of work and a big chance for human error.  You create a table like I mentioned then you only have to update a few entries in the table.  Your function never has to be touched.  I know the chances of that happening are slim, again IMHO I think your code makes it harder than it needs to be.  I guess I couldn't stay out of it. 🙂

    Nah... If I were afraid of a few bumps & bruises or afraid of a little  criticism, this is the last place I'd come... 
    Yes, I started the thread because I had hit a wall and couldn't see a solution... Now that I feel like I have a good solution, I'm looking for someone to see the thing (or things) that I missed and blow my doors off... At the end of the day, I simply want the best solution I can get my hands on.
    So, in that vein... If your way is the better way, I will change gears and go that way... But not without hard numbers... My best cold cache number so far is below...

    -- SET STATISTICS TIME ON;
    -- SET STATISTICS IO OFF;
    -- DBCC DROPCLEANBUFFERS;
    -- DBCC FREEPROCCACHE;

    SQL Server Execution Times:
     CPU time = 109 ms, elapsed time = 115 ms..

  • Jason A. Long - Friday, October 20, 2017 3:45 PM

    below86 - Friday, October 20, 2017 2:47 PM

    Jason A. Long - Friday, October 20, 2017 2:27 PM

    below86 - Friday, October 20, 2017 1:44 PM

    IMHO this is a long way around the barn to get what you asked for.

    How so?

    You want the number of working days between two dates, excluding weekends and holidays. Right?  You already know what days are weekends and what days are holidays.  Why make it calculate it each time you run the function?  What happens in your code if you have a new holiday to add?  Let's say you start recognizing presidents day as a holiday in 2018(02/19/2018).  You need to change both case statements by adding this date, then you need to increment all of your numbers in your THEN statement after this date.  Seems like a lot of work and a big chance for human error.  You create a table like I mentioned then you only have to update a few entries in the table.  Your function never has to be touched.  I know the chances of that happening are slim, again IMHO I think your code makes it harder than it needs to be.  I guess I couldn't stay out of it. 🙂

    Nah... If I were afraid of a few bumps & bruises or afraid of a little  criticism, this is the last place I'd come... 
    Yes, I started the thread because I had hit a wall and couldn't see a solution... Now that I feel like I have a good solution, I'm looking for someone to see the thing (or things) that I missed and blow my doors off... At the end of the day, I simply want the best solution I can get my hands on.
    So, in that vein... If your way is the better way, I will change gears and go that way... But not without hard numbers... My best cold cache number so far is below...

    -- SET STATISTICS TIME ON;
    -- SET STATISTICS IO OFF;
    -- DBCC DROPCLEANBUFFERS;
    -- DBCC FREEPROCCACHE;

    SQL Server Execution Times:
     CPU time = 109 ms, elapsed time = 115 ms..

    That's damned impressive.  Is that on your original million row test table?

    --Jeff Moden


    RBAR is pronounced "ree-bar" and is a "Modenism" for Row-By-Agonizing-Row.
    First step towards the paradigm shift of writing Set Based code:
    ________Stop thinking about what you want to do to a ROW... think, instead, of what you want to do to a COLUMN.
    "Change is inevitable... change for the better is not".

    Helpful Links:
    How to post code problems
    How to Post Performance Problems
    Create a Tally Function (fnTally)
    Intro to Tally Tables and Functions

  • Jeff Moden - Friday, October 20, 2017 3:38 PM

    below86 - Friday, October 20, 2017 2:47 PM

    Jason A. Long - Friday, October 20, 2017 2:27 PM

    below86 - Friday, October 20, 2017 1:44 PM

    IMHO this is a long way around the barn to get what you asked for.

    How so?

    You want the number of working days between two dates, excluding weekends and holidays. Right?  You already know what days are weekends and what days are holidays.  Why make it calculate it each time you run the function?  What happens in your code if you have a new holiday to add?  Let's say you start recognizing presidents day as a holiday in 2018(02/19/2018).  You need to change both case statements by adding this date, then you need to increment all of your numbers in your THEN statement after this date.  Seems like a lot of work and a big chance for human error.  You create a table like I mentioned then you only have to update a few entries in the table.  Your function never has to be touched.  I know the chances of that happening are slim, again IMHO I think your code makes it harder than it needs to be.  I guess I couldn't stay out of it. 🙂

    That has been pretty much my thoughts about this.  I hate to have to change code to add even 1 new holiday for 1 year.

    I have an old trick with a Calendar table that I've not had time to test for this.  I'll give it a run this weekend and see.  I don't know if it'll be able to beat 3 seconds on a million rows but it'll make maintenance a breeze.  As for those that think holidays and workdays don't change much, you should work where I work. 😉

    It's all a matter of perspective... I refactored a 60+ million row table last weekend  that took right at 12 hours because a client wanted 3 holidays changed... Simply adding or removing a few rows in a big case expression, a hand full of different functions, sounds like absolute nirvana to me.

  • Jeff Moden - Friday, October 20, 2017 3:56 PM

    Jason A. Long - Friday, October 20, 2017 3:45 PM

    below86 - Friday, October 20, 2017 2:47 PM

    Jason A. Long - Friday, October 20, 2017 2:27 PM

    below86 - Friday, October 20, 2017 1:44 PM

    IMHO this is a long way around the barn to get what you asked for.

    How so?

    You want the number of working days between two dates, excluding weekends and holidays. Right?  You already know what days are weekends and what days are holidays.  Why make it calculate it each time you run the function?  What happens in your code if you have a new holiday to add?  Let's say you start recognizing presidents day as a holiday in 2018(02/19/2018).  You need to change both case statements by adding this date, then you need to increment all of your numbers in your THEN statement after this date.  Seems like a lot of work and a big chance for human error.  You create a table like I mentioned then you only have to update a few entries in the table.  Your function never has to be touched.  I know the chances of that happening are slim, again IMHO I think your code makes it harder than it needs to be.  I guess I couldn't stay out of it. 🙂

    Nah... If I were afraid of a few bumps & bruises or afraid of a little  criticism, this is the last place I'd come... 
    Yes, I started the thread because I had hit a wall and couldn't see a solution... Now that I feel like I have a good solution, I'm looking for someone to see the thing (or things) that I missed and blow my doors off... At the end of the day, I simply want the best solution I can get my hands on.
    So, in that vein... If your way is the better way, I will change gears and go that way... But not without hard numbers... My best cold cache number so far is below...

    -- SET STATISTICS TIME ON;
    -- SET STATISTICS IO OFF;
    -- DBCC DROPCLEANBUFFERS;
    -- DBCC FREEPROCCACHE;

    SQL Server Execution Times:
     CPU time = 109 ms, elapsed time = 115 ms..

    That's damned impressive.  Is that on your original million row test table?

    Yes it is... All of the times posted up to now has included both the capture of the actual plan and SET STATISTICS IO,TIME ON;
    That was the first time with  everything turned off except SET STATISTICS TIME ON;

    Probably shouldn't exclude parse & compile if there is going to be a distinction between warm and cold cache times...

    SQL Server parse and compile time:
     CPU time = 16 ms, elapsed time = 52 ms.

    SQL Server Execution Times:
     CPU time = 109 ms, elapsed time = 115 ms.

    @jeff... I meant to ask you... Would it be a major breach of etiquette if I were to "borrow" the test harness you posted here? https://qa.sqlservercentral.com/Forums/FindPost1898722.aspx
    I like the output format much better than what I've been using so far.

  • Jason A. Long - Friday, October 20, 2017 4:07 PM

    @jeff... I meant to ask you... Would it be a major breach of etiquette if I were to "borrow" the test harness you posted here? https://qa.sqlservercentral.com/Forums/FindPost1898722.aspx
    I like the output format much better than what I've been using so far.

    I'm honored that you think so.   "You may fire when ready, Mr. Gridley". 😉

    --Jeff Moden


    RBAR is pronounced "ree-bar" and is a "Modenism" for Row-By-Agonizing-Row.
    First step towards the paradigm shift of writing Set Based code:
    ________Stop thinking about what you want to do to a ROW... think, instead, of what you want to do to a COLUMN.
    "Change is inevitable... change for the better is not".

    Helpful Links:
    How to post code problems
    How to Post Performance Problems
    Create a Tally Function (fnTally)
    Intro to Tally Tables and Functions

  • While we're at it... when all this first started and while I was waiting for your Tally function, I cranked this out to generate test data.  I normally resort to the Cross Join type of pseudo-cursor for forum work so that people don't have to worry about having a Tally function or table.  It's what I've been using to test with.


    --===== Test data control variables and presets
    DECLARE  @LoDT DATETIME = '2000' --Inclusive
            ,@HiDT DATETIME = '2030' --Exclusive
            ,@MaxSpan INT   = 90 --Max number of days in period
            ,@Rows INT      = 1000000
            ,@Days INT

    ;
     SELECT @Days = DATEDIFF(dd,@LoDT,@HiDT)-@MaxSpan
    ;
    --===== Create the test table (added a column just to see)
         IF OBJECT_ID('tempdb..#TestData', 'U') IS NOT NULL
       DROP TABLE #TestData
    ;
     CREATE TABLE #TestData
            (
             RN     INT         NOT NULL
            ,beg_dt DATETIME    NOT NULL
            ,end_dt DATETIME    NOT NULL
            ,Span   AS CONVERT(FLOAT,end_dt-beg_dt) PERSISTED
             PRIMARY KEY CLUSTERED (beg_dt, end_dt)
                WITH (IGNORE_DUP_KEY = ON)
            )
    ;
    --===== Use "Minimal Logging" to populate the table with random "begin" dates
         -- and a random span to create the random "end" dates.
       WITH
    cteGenDate AS
    (
     SELECT TOP (@Rows)
            beg_dt = RAND(CHECKSUM(NEWID()))*@Days+@LoDT --Gotta love direct date math.
       FROM      sys.all_columns ac1
      CROSS JOIN sys.all_columns ac2
    )
     INSERT INTO #TestData WITH(TABLOCK) --For Minimal Logging
            (RN, beg_dt, end_dt)   
     SELECT  RN     = ROW_NUMBER() OVER (ORDER BY beg_dt)
            ,beg_dt
            ,end_dt = RAND(CHECKSUM(NEWID()))*@MaxSpan+beg_dt
       FROM cteGenDate
      ORDER BY beg_dt, end_dt   --For Minimal Logging with Clustered Index
     OPTION (RECOMPILE)         --For Minimal Logging
    ;

    --Jeff Moden


    RBAR is pronounced "ree-bar" and is a "Modenism" for Row-By-Agonizing-Row.
    First step towards the paradigm shift of writing Set Based code:
    ________Stop thinking about what you want to do to a ROW... think, instead, of what you want to do to a COLUMN.
    "Change is inevitable... change for the better is not".

    Helpful Links:
    How to post code problems
    How to Post Performance Problems
    Create a Tally Function (fnTally)
    Intro to Tally Tables and Functions

  • Jeff Moden - Friday, October 20, 2017 7:31 PM

    Jason A. Long - Friday, October 20, 2017 4:07 PM

    @jeff... I meant to ask you... Would it be a major breach of etiquette if I were to "borrow" the test harness you posted here? https://qa.sqlservercentral.com/Forums/FindPost1898722.aspx
    I like the output format much better than what I've been using so far.

    I'm honored that you think so.   "You may fire when ready, Mr. Gridley". 😉

    Thank you Commodore! You sir are a true gentleman and a scholar.

  • Jeff Moden - Friday, October 20, 2017 7:47 PM

    While we're at it... when all this first started and while I was waiting for your Tally function, I cranked this out to generate test data.  I normally resort to the Cross Join type of pseudo-cursor for forum work so that people don't have to worry about having a Tally function or table.  It's what I've been using to test with.


    --===== Test data control variables and presets
    DECLARE  @LoDT DATETIME = '2000' --Inclusive
            ,@HiDT DATETIME = '2030' --Exclusive
            ,@MaxSpan INT   = 90 --Max number of days in period
            ,@Rows INT      = 1000000
            ,@Days INT

    ;
     SELECT @Days = DATEDIFF(dd,@LoDT,@HiDT)-@MaxSpan
    ;
    --===== Create the test table (added a column just to see)
         IF OBJECT_ID('tempdb..#TestData', 'U') IS NOT NULL
       DROP TABLE #TestData
    ;
     CREATE TABLE #TestData
            (
             RN     INT         NOT NULL
            ,beg_dt DATETIME    NOT NULL
            ,end_dt DATETIME    NOT NULL
            ,Span   AS CONVERT(FLOAT,end_dt-beg_dt) PERSISTED
             PRIMARY KEY CLUSTERED (beg_dt, end_dt)
                WITH (IGNORE_DUP_KEY = ON)
            )
    ;
    --===== Use "Minimal Logging" to populate the table with random "begin" dates
         -- and a random span to create the random "end" dates.
       WITH
    cteGenDate AS
    (
     SELECT TOP (@Rows)
            beg_dt = RAND(CHECKSUM(NEWID()))*@Days+@LoDT --Gotta love direct date math.
       FROM      sys.all_columns ac1
      CROSS JOIN sys.all_columns ac2
    )
     INSERT INTO #TestData WITH(TABLOCK) --For Minimal Logging
            (RN, beg_dt, end_dt)   
     SELECT  RN     = ROW_NUMBER() OVER (ORDER BY beg_dt)
            ,beg_dt
            ,end_dt = RAND(CHECKSUM(NEWID()))*@MaxSpan+beg_dt
       FROM cteGenDate
      ORDER BY beg_dt, end_dt   --For Minimal Logging with Clustered Index
     OPTION (RECOMPILE)         --For Minimal Logging
    ;

    Now this has some interesting stuff going on... You definitely took a more measured approach than I did.
    I just picked a date close to middle on the Calendar table, added two "CHECKSUM(NEWID()) % 999999" and simply added or subtracted 9's until it produced legitimate looking ranges.
    This... "beg_dt = RAND(CHECKSUM(NEWID()))*@Days+@LoDT  " ...  This took me a few minutes...I don't ever use RAND() and either didn't know or completely forgot that generates a float <= 1.
    For a second I thought you were using voodoo to constrain the output range... I'll have to spend some time using it to see if I like it as much as the % method (thank you for that one too BTW)
    Normally if I need "believable" test data I usually do something like this...
    SELECT TOP 1000
        ROW_NUMBER() OVER (ORDER BY c.object_id),
        DATEADD(DAY, ld.low_day, '20100101'),
        DATEADD(DAY, hd.high_day, '20100101')
    FROM
        sys.columns c    --< this is a lie... IRL it would be a tally...
        CROSS APPLY ( VALUES (ABS(CHECKSUM(NEWID())) % 1460) ) ld (low_day)                    -- 0-4 years
        CROSS APPLY ( VALUES (ABS(CHECKSUM(NEWID())) % (1825-ld.low_day) ) ) hd (high_day);    -- low-day-5 years

    You don't have to sell me on the direct date math or the INT math... there have been a few occasions that the INT math had me feeling like a 4 yr old at a magic show.
    Sadly, for whatever reason, they aren't solutions that usually hit me right off the bat.

    While on the topic of cool tricks, In the other test harness... I noticed you have a  CHECKPOINT... 
    It makes me suspect that you're grabbing test results from the transaction logs... If so, what's your verdict?
    I've been kicking around the idea of using extended events to capture test results, hopefully with less "observer effect" than plan capture & SET STATISTICS IO,TIME ON;
    https://docs.microsoft.com/en-us/sql/relational-databases/extended-events/quick-start-extended-events-in-sql-server

    Once again, thank you!

  • Well... I really am a big dumb animal... I forgot to unplug the table's RN column after running the times for the standalone table... And, we're back to previous numbers... :blush:

  • Jason A. Long - Friday, October 20, 2017 11:27 PM

    Jeff Moden - Friday, October 20, 2017 7:47 PM

    While we're at it... when all this first started and while I was waiting for your Tally function, I cranked this out to generate test data.  I normally resort to the Cross Join type of pseudo-cursor for forum work so that people don't have to worry about having a Tally function or table.  It's what I've been using to test with.


    --===== Test data control variables and presets
    DECLARE  @LoDT DATETIME = '2000' --Inclusive
            ,@HiDT DATETIME = '2030' --Exclusive
            ,@MaxSpan INT   = 90 --Max number of days in period
            ,@Rows INT      = 1000000
            ,@Days INT

    ;
     SELECT @Days = DATEDIFF(dd,@LoDT,@HiDT)-@MaxSpan
    ;
    --===== Create the test table (added a column just to see)
         IF OBJECT_ID('tempdb..#TestData', 'U') IS NOT NULL
       DROP TABLE #TestData
    ;
     CREATE TABLE #TestData
            (
             RN     INT         NOT NULL
            ,beg_dt DATETIME    NOT NULL
            ,end_dt DATETIME    NOT NULL
            ,Span   AS CONVERT(FLOAT,end_dt-beg_dt) PERSISTED
             PRIMARY KEY CLUSTERED (beg_dt, end_dt)
                WITH (IGNORE_DUP_KEY = ON)
            )
    ;
    --===== Use "Minimal Logging" to populate the table with random "begin" dates
         -- and a random span to create the random "end" dates.
       WITH
    cteGenDate AS
    (
     SELECT TOP (@Rows)
            beg_dt = RAND(CHECKSUM(NEWID()))*@Days+@LoDT --Gotta love direct date math.
       FROM      sys.all_columns ac1
      CROSS JOIN sys.all_columns ac2
    )
     INSERT INTO #TestData WITH(TABLOCK) --For Minimal Logging
            (RN, beg_dt, end_dt)   
     SELECT  RN     = ROW_NUMBER() OVER (ORDER BY beg_dt)
            ,beg_dt
            ,end_dt = RAND(CHECKSUM(NEWID()))*@MaxSpan+beg_dt
       FROM cteGenDate
      ORDER BY beg_dt, end_dt   --For Minimal Logging with Clustered Index
     OPTION (RECOMPILE)         --For Minimal Logging
    ;

    Now this has some interesting stuff going on... You definitely took a more measured approach than I did.
    I just picked a date close to middle on the Calendar table, added two "CHECKSUM(NEWID()) % 999999" and simply added or subtracted 9's until it produced legitimate looking ranges.
    This... "beg_dt = RAND(CHECKSUM(NEWID()))*@Days+@LoDT  " ...  This took me a few minutes...I don't ever use RAND() and either didn't know or completely forgot that generates a float <= 1.
    For a second I thought you were using voodoo to constrain the output range... I'll have to spend some time using it to see if I like it as much as the % method (thank you for that one too BTW)
    Normally if I need "believable" test data I usually do something like this...
    SELECT TOP 1000
        ROW_NUMBER() OVER (ORDER BY c.object_id),
        DATEADD(DAY, ld.low_day, '20100101'),
        DATEADD(DAY, hd.high_day, '20100101')
    FROM
        sys.columns c    --< this is a lie... IRL it would be a tally...
        CROSS APPLY ( VALUES (ABS(CHECKSUM(NEWID())) % 1460) ) ld (low_day)                    -- 0-4 years
        CROSS APPLY ( VALUES (ABS(CHECKSUM(NEWID())) % (1825-ld.low_day) ) ) hd (high_day);    -- low-day-5 years

    You don't have to sell me on the direct date math or the INT math... there have been a few occasions that the INT math had me feeling like a 4 yr old at a magic show.
    Sadly, for whatever reason, they aren't solutions that usually hit me right off the bat.

    While on the topic of cool tricks, In the other test harness... I noticed you have a  CHECKPOINT... 
    It makes me suspect that you're grabbing test results from the transaction logs... If so, what's your verdict?
    I've been kicking around the idea of using extended events to capture test results, hopefully with less "observer effect" than plan capture & SET STATISTICS IO,TIME ON;
    https://docs.microsoft.com/en-us/sql/relational-databases/extended-events/quick-start-extended-events-in-sql-server

    Once again, thank you!

    Thanks for the feedback, Jason.  Always a pleasure to "talk" with you.

    I don't know if you're ever seen the following two articles on the subject of the generation of random but constrained test data.  Some of the things you've pointed out are covered in these articles.

    Generating Test Data: Part 1 - Generating Random Integers and Floats
    Generating Test Data: Part 2 - Generating Sequential and Random Dates

    For the integer stuff in those two articles, I need to go back someday and make a correction because there is a 1 in 4 Billion chance (and I should have played the lottery on the days that I have hit that chance)  that the largest negative number will appear and the ABSolute causes a failure on that.  The fix is to do the Modulo before you do the ABS.

    Dwain Camps (miss you alot, ol' friend) wrote some pretty good articles on the non-linear generation of random constrained data at the following link.

    Generating Non-uniform Random Numbers with SQL

    None of the graphics he posted in the article have survived because they were on another web site but it's a great article.

    As for the CHECKPOINT thing... I actually left those in that particular harness by mistake.  I wanted to make sure that having stuff in write-cache waiting to be written to disk wasn't the cause of something I was seeing (and can't remember what that was, now) in early testing.

    As for Extended Events... that might be fine for personal testing just like using SQL Profiler or some third party tool might be fine but I generally try to make the test harnesses that I post on the forum 100% stand-alone so that no special skills or software are required on the part of the reader to duplicate the tests.  I do make sure that the "observer effect" is minimal where I use SET STATISTICS or I'll use something else.  The case of performance testing non-inline user defined functions is a good case where I won't use SET STATISTICS because of the extreme observer effect on those.

    --Jeff Moden


    RBAR is pronounced "ree-bar" and is a "Modenism" for Row-By-Agonizing-Row.
    First step towards the paradigm shift of writing Set Based code:
    ________Stop thinking about what you want to do to a ROW... think, instead, of what you want to do to a COLUMN.
    "Change is inevitable... change for the better is not".

    Helpful Links:
    How to post code problems
    How to Post Performance Problems
    Create a Tally Function (fnTally)
    Intro to Tally Tables and Functions

  • Jason A. Long - Saturday, October 21, 2017 2:07 AM

    Well... I really am a big dumb animal... I forgot to unplug the table's RN column after running the times for the standalone table... And, we're back to previous numbers... :blush:

    What does that mean?  What's the run time that we're actually talking about now?

    --Jeff Moden


    RBAR is pronounced "ree-bar" and is a "Modenism" for Row-By-Agonizing-Row.
    First step towards the paradigm shift of writing Set Based code:
    ________Stop thinking about what you want to do to a ROW... think, instead, of what you want to do to a COLUMN.
    "Change is inevitable... change for the better is not".

    Helpful Links:
    How to post code problems
    How to Post Performance Problems
    Create a Tally Function (fnTally)
    Intro to Tally Tables and Functions

Viewing 15 posts - 31 through 45 (of 92 total)

You must be logged in to reply to this topic. Login to reply