├── .gitignore ├── screenshots ├── .DS_Store ├── granularity.png ├── custom-periods.png ├── multiple-periods.png └── templated-periods.png ├── your_view_name.view.lkml ├── your_model.model.lkml ├── README.md └── _date_comparison.view.lkml /.gitignore: -------------------------------------------------------------------------------- 1 | screenshots/.DS_Store 2 | -------------------------------------------------------------------------------- /screenshots/.DS_Store: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/teamdatatonic/looker-date-comparison/HEAD/screenshots/.DS_Store -------------------------------------------------------------------------------- /screenshots/granularity.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/teamdatatonic/looker-date-comparison/HEAD/screenshots/granularity.png -------------------------------------------------------------------------------- /screenshots/custom-periods.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/teamdatatonic/looker-date-comparison/HEAD/screenshots/custom-periods.png -------------------------------------------------------------------------------- /screenshots/multiple-periods.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/teamdatatonic/looker-date-comparison/HEAD/screenshots/multiple-periods.png -------------------------------------------------------------------------------- /screenshots/templated-periods.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/teamdatatonic/looker-date-comparison/HEAD/screenshots/templated-periods.png -------------------------------------------------------------------------------- /your_view_name.view.lkml: -------------------------------------------------------------------------------- 1 | include: "_date_comparison.view.lkml" 2 | 3 | view: your_view_name { 4 | 5 | #---- This is an example view file 6 | dimension_group: your_date_dimension { 7 | type: time 8 | timeframes: [date, raw] 9 | } 10 | 11 | 12 | #---- Add in these parameters to your own view file replacing your_date_dimension 13 | extends: [_date_comparison] 14 | 15 | dimension: event_raw{ 16 | sql: ${your_date_dimension_time::datetime} ;; 17 | type: date_raw 18 | hidden: yes 19 | } 20 | 21 | } 22 | -------------------------------------------------------------------------------- /your_model.model.lkml: -------------------------------------------------------------------------------- 1 | connection: "sandbox" 2 | 3 | # include all the views 4 | include: "*.view" 5 | 6 | explore: your_view_name { 7 | #---- This is an example explore file 8 | 9 | 10 | #---- Add in this sql_always_where clause replacing your_view_name 11 | sql_always_where: 12 | {% if your_view_name.current_date_range._is_filtered %} 13 | {% condition your_view_name.current_date_range %} ${event_raw} {% endcondition %} 14 | 15 | {% if your_view_name.previous_date_range._is_filtered or your_view_name.compare_to._in_query %} 16 | {% if your_view_name.comparison_periods._parameter_value == "2" %} 17 | or 18 | ${event_raw} between ${period_2_start} and ${period_2_end} 19 | 20 | {% elsif your_view_name.comparison_periods._parameter_value == "3" %} 21 | or 22 | ${event_raw} between ${period_2_start} and ${period_2_end} 23 | or 24 | ${event_raw} between ${period_3_start} and ${period_3_end} 25 | 26 | 27 | {% elsif your_view_name.comparison_periods._parameter_value == "4" %} 28 | or 29 | ${event_raw} between ${period_2_start} and ${period_2_end} 30 | or 31 | ${event_raw} between ${period_3_start} and ${period_3_end} 32 | or 33 | ${event_raw} between ${period_4_start} and ${period_4_end} 34 | 35 | {% else %} 1 = 1 36 | {% endif %} 37 | {% endif %} 38 | {% else %} 1 = 1 39 | {% endif %};; 40 | 41 | } 42 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Date Comparison Block 2 | 3 | ## What is this block? 4 | This block allows for comparisons from one date period to another, or from one date period to a number of periods. It provides extensive flexibility in comparing date ranges, and presenting these back as a single visualisation. 5 | 6 | 7 | ## Motivation 8 | There are already a few blocks that allow for comparison between periods in Looker. The motivation for this block was to combine all the elements from the other blocks in a way that is intuitive, flexible and fast to use. 9 | 10 | 11 | ## Solution 12 | The approach was to use the intuitive style of date comparison that is used in Google Analytics - where the basic idea is that you choose the current date range that you’re interested in, then choose the comparison date range in the past. There are some templated period filters to make this easy for the user, but also custom filters to allow flexibility in comparing dates. 13 | The features are: 14 | 15 | 1. **Templated comparison periods** - Once the initial period of interest is chosen, rather than manually choose the period before, there is a range of options presented such as “Previous Period”, “Previous Month”, “Previous Year” etc 16 | ![](screenshots/templated-periods.png) 17 | 18 | 2. **Custom comparison periods** - in the situation where any of the above don’t fit what you are trying to see, then a custom previous date range may be chosen 19 | ![](screenshots/custom-periods.png) 20 | 21 | 3. **Any granularity** - Choose how granular you want your results to be by picking the appropriate date dimension from the dimension group 22 | 23 | ![](screenshots/granularity.png) 24 | 25 | 4. **Multiple periods** - Choose the number of periods you would like to compare, this is only available for templated periods, e.g January this year vs January last year vs January 2 years ago etc. 26 | ![](screenshots/multiple-periods.png) 27 | 28 | ## How to use 29 | The process for using this in your explore is as follows: 30 | 31 | 1. Add the filter `Date Range` to choose your initial date range 32 | 2. Add the filter `Compare To (Templated)` to choose a templated comparison range 33 | **OR** 34 | Add the filter `Compare To (Custom)` to choose a custom comparison range 35 | 4. If you have chosen a templated range, you may choose to add more periods by using the `Comparison Periods` filter 36 | 5. Choose your date dimension - only pick from the dimension group `Current Period Date`, don’t use any date dimension from any other view 37 | 6. Choose your other dimensions and measures as usual 38 | 7. Finally, pivot on `Period` 39 | 8. Hit run 40 | 41 | 42 | ## How to implement 43 | Syntax assumes a BigQuery connection, you may need to adjust this for other database connections. 44 | To have this available to use in your explores and dashboards there are a few steps: 45 | 46 | 1. Copy the view file `_date_comparison.view.lkml` into your project 47 | 2. In the view file where the date dimension you would like to be able to compare is, extend the `_date_comparison` view by adding the parameter `extends: [_date_comparison]` 48 | 3. In the same view file, add one new dimensions, `event_raw`. This is simply `::datetime`. This step is just so that naming convention used in the `_date_comparison` view works correctly 49 | 3. In the relevant explore LookML, add in the `sql_always_where` clause defined in the model file here. Replace all instances of `` with your view name. 50 | 51 | ## Trouble Shooting 52 | 53 | If you have you connection set up to a timezone and time selected e.g. UTC, US Central Time, Looker will pass additional arguements into the SQL which will error with the old code block, this is due to the use of the Date_sub function. 54 | The following error will occur: 55 | `No matching signature for function FORMAT_TIMESTAMP for argument types: STRING, DATE, STRING. Supported signature: FORMAT_TIMESTAMP(STRING, TIMESTAMP, [STRING]) at` 56 | 57 | Please ensure your code is up to date with our master branch, which now has implemented fixes for this. 58 | -------------------------------------------------------------------------------- /_date_comparison.view.lkml: -------------------------------------------------------------------------------- 1 | # this is the code for the date comparison tool, which mimics what google 360 does in the browser in comparing two different date ranges. use with _date_dim.view.lkml 2 | view: _date_comparison { 3 | # label: "Timeline Comparison Fields" 4 | 5 | extension: required 6 | filter: current_date_range { 7 | view_label: "Timeline Comparison Fields" 8 | label: "1. Date Range" 9 | description: "Select the date range you are interested in using this filter, can be used by itself. Make sure any filter on Event Date covers this period, or is removed." 10 | type: date 11 | convert_tz: yes 12 | } 13 | filter: previous_date_range { 14 | view_label: "Timeline Comparison Fields" 15 | label: "2b. Compare To (Custom):" 16 | group_label: "Compare to:" 17 | 18 | description: "Use this if you want to specify a custom date range to compare to (limited to 2 comparison periods). Always use with '1. Date Range' filter (or it will error). Make sure any filter on Event Date covers this period, or is removed." 19 | 20 | type: date 21 | convert_tz: yes 22 | } 23 | 24 | dimension_group: in_period { 25 | type: duration 26 | intervals: [day] 27 | description: "Gives the number of days in the current period date range" 28 | sql_start: {% date_start current_date_range %} ;; 29 | sql_end: {% date_end current_date_range %} ;; 30 | hidden: yes 31 | } 32 | 33 | dimension: period_2_start { 34 | view_label: "Timeline Comparison Fields" 35 | description: "Calculates the start of the previous period" 36 | type: date_raw 37 | sql: 38 | {% if compare_to._in_query %} 39 | {% if compare_to._parameter_value == "Period" %} 40 | TIMESTAMP_SUB({% date_start current_date_range %} , INTERVAL ${days_in_period} DAY) 41 | {% else %} 42 | TIMESTAMP(DATETIME_SUB(DATETIME({% date_start current_date_range %}) , INTERVAL 1 {% parameter compare_to %})) 43 | {% endif %} 44 | {% else %} 45 | {% date_start previous_date_range %} 46 | {% endif %};; 47 | hidden: yes 48 | } 49 | 50 | dimension: period_2_end { 51 | view_label: "Timeline Comparison Fields" 52 | description: "Calculates the end of the previous period" 53 | type: date_raw 54 | sql: 55 | {% if compare_to._in_query %} 56 | {% if compare_to._parameter_value == "Period" %} 57 | TIMESTAMP_SUB({% date_start current_date_range %}, INTERVAL 1 DAY) 58 | {% else %} 59 | TIMESTAMP(DATETIME_SUB(DATETIME_SUB(DATETIME({% date_end current_date_range %}), INTERVAL 1 DAY), INTERVAL 1 {% parameter compare_to %})) 60 | {% endif %} 61 | {% else %} 62 | {% date_end previous_date_range %} 63 | {% endif %};; 64 | hidden: yes 65 | } 66 | 67 | dimension: period_3_start { 68 | view_label: "Timeline Comparison Fields" 69 | description: "Calculates the start of 2 periods ago" 70 | type: date_raw 71 | sql: 72 | {% if compare_to._parameter_value == "Period" %} 73 | TIMESTAMP_SUB({% date_start current_date_range %}, INTERVAL 2*${days_in_period} DAY) 74 | {% else %} 75 | TIMESTAMP(DATETIME_SUB(DATETIME({% date_start current_date_range %}), INTERVAL 2 {% parameter compare_to %})) 76 | {% endif %};; 77 | hidden: yes 78 | 79 | } 80 | 81 | dimension: period_3_end { 82 | view_label: "Timeline Comparison Fields" 83 | description: "Calculates the end of 2 periods ago" 84 | type: date_raw 85 | sql: 86 | {% if compare_to._parameter_value == "Period" %} 87 | TIMESTAMP_SUB(${period_2_start}, INTERVAL 1 DAY) 88 | {% else %} 89 | TIMESTAMP(DATETIME_SUB(DATETIME_SUB(DATETIME({% date_end current_date_range %}), INTERVAL 1 DAY), INTERVAL 2 {% parameter compare_to %})) 90 | {% endif %};; 91 | hidden: yes 92 | } 93 | 94 | dimension: period_4_start { 95 | view_label: "Timeline Comparison Fields" 96 | description: "Calculates the start of 4 periods ago" 97 | type: date_raw 98 | sql: 99 | {% if compare_to._parameter_value == "Period" %} 100 | TIMESTAMP_SUB({% date_start current_date_range %}, INTERVAL 3*${days_in_period} DAY) 101 | {% else %} 102 | TIMESTAMP(DATETIME_SUB(DATETIME({% date_start current_date_range %}), INTERVAL 3 {% parameter compare_to %})) 103 | {% endif %};; 104 | hidden: yes 105 | } 106 | 107 | dimension: period_4_end { 108 | view_label: "Timeline Comparison Fields" 109 | description: "Calculates the end of 4 periods ago" 110 | type: date_raw 111 | sql: 112 | {% if compare_to._parameter_value == "Period" %} 113 | TIMESTAMP_SUB(${period_2_start}, INTERVAL 1 DAY) 114 | {% else %} 115 | TIMESTAMP(DATETIME_SUB(DATETIME_SUB(DATETIME({% date_end current_date_range %}), INTERVAL 1 DAY), INTERVAL 3 {% parameter compare_to %})) 116 | {% endif %};; 117 | hidden: yes 118 | } 119 | 120 | parameter: compare_to { 121 | description: "Choose the period you would like to compare to. Must be used with Current Date Range filter" 122 | label: "2a. Compare To (Templated):" 123 | type: unquoted 124 | allowed_value: { 125 | label: "Previous Period" 126 | value: "Period" 127 | } 128 | allowed_value: { 129 | label: "Previous Week" 130 | value: "Week" 131 | } 132 | allowed_value: { 133 | label: "Previous Month" 134 | value: "Month" 135 | } 136 | allowed_value: { 137 | label: "Previous Quarter" 138 | value: "Quarter" 139 | } 140 | allowed_value: { 141 | label: "Previous Year" 142 | value: "Year" 143 | } 144 | default_value: "Period" 145 | view_label: "Timeline Comparison Fields" 146 | } 147 | 148 | parameter: comparison_periods { 149 | label: "3. Number of Periods" 150 | description: "Choose the number of periods you would like to compare - defaults to 2. Only works with templated periods from step 2." 151 | type: unquoted 152 | allowed_value: { 153 | label: "2" 154 | value: "2" 155 | } 156 | allowed_value: { 157 | label: "3" 158 | value: "3" 159 | } 160 | allowed_value: { 161 | label: "4" 162 | value: "4" 163 | } 164 | default_value: "2" 165 | view_label: "Timeline Comparison Fields" 166 | } 167 | 168 | dimension: period { 169 | view_label: "Timeline Comparison Fields" 170 | label: "Period" 171 | description: "Pivot me! Returns the period the metric covers, i.e. either the 'This Period', 'Previous Period' or '3 Periods Ago'" 172 | type: string 173 | order_by_field: order_for_period 174 | sql: 175 | {% if current_date_range._is_filtered %} 176 | CASE 177 | WHEN {% condition current_date_range %} ${event_raw} {% endcondition %} 178 | THEN "This {% parameter compare_to %}" 179 | WHEN ${event_raw} between ${period_2_start} and ${period_2_end} 180 | THEN "Last {% parameter compare_to %}" 181 | WHEN ${event_raw} between ${period_3_start} and ${period_3_end} 182 | THEN "2 {% parameter compare_to %}s Ago" 183 | WHEN ${event_raw} between ${period_4_start} and ${period_4_end} 184 | THEN "3 {% parameter compare_to %}s Ago" 185 | END 186 | {% else %} 187 | NULL 188 | {% endif %} 189 | ;; 190 | } 191 | 192 | dimension: order_for_period { 193 | hidden: yes 194 | view_label: "Timeline Comparison Fields" 195 | label: "Period" 196 | description: "Pivot me! Returns the period the metric covers, i.e. either the 'This Period', 'Previous Period' or '3 Periods Ago'" 197 | type: string 198 | sql: 199 | {% if current_date_range._is_filtered %} 200 | CASE 201 | WHEN {% condition current_date_range %} ${event_raw} /*findme6*/{% endcondition %} 202 | THEN 1 203 | WHEN ${event_raw} between ${period_2_start} and ${period_2_end} 204 | THEN 2 205 | WHEN ${event_raw} between ${period_3_start} and ${period_3_end} 206 | THEN 3 207 | WHEN ${event_raw} between ${period_4_start} and ${period_4_end} 208 | THEN 4 209 | END 210 | {% else %} 211 | NULL 212 | {% endif %} 213 | ;; 214 | } 215 | 216 | dimension_group: date_in_period { 217 | description: "Use this as your date dimension when comparing periods. Aligns the all previous periods onto the current period" 218 | label: "Current Period" 219 | type: time 220 | sql: TIMESTAMP_ADD({% date_start current_date_range %},INTERVAL (${day_in_period}-1) DAY) ;; 221 | view_label: "Timeline Comparison Fields" 222 | timeframes: [date, week, month, quarter, year] 223 | } 224 | 225 | dimension: day_in_period { 226 | view_label: "Timeline Comparison Fields" 227 | description: "Gives the number of days since the start of each periods. Use this to align the event dates onto the same axis, the axes will read 1,2,3, etc." 228 | type: number 229 | sql: 230 | {% if current_date_range._is_filtered %} 231 | CASE 232 | WHEN {% condition current_date_range %} ${event_raw} {% endcondition %} 233 | THEN TIMESTAMP_DIFF(${event_raw},{% date_start current_date_range %},DAY)+1 234 | 235 | WHEN ${event_raw} between ${period_2_start} and ${period_2_end} 236 | THEN TIMESTAMP_DIFF(${event_raw}, ${period_2_start},DAY)+1 237 | 238 | WHEN ${event_raw} between ${period_3_start} and ${period_3_end} 239 | THEN TIMESTAMP_DIFF(${event_raw}, ${period_3_start},DAY)+1 240 | 241 | WHEN ${event_raw} between ${period_4_start} and ${period_4_end} 242 | THEN TIMESTAMP_DIFF(${event_raw}, ${period_4_start},DAY)+1 243 | END 244 | 245 | {% else %} NULL 246 | {% endif %} 247 | ;; 248 | hidden: no 249 | } 250 | 251 | } 252 | --------------------------------------------------------------------------------