├── .github └── data-source-template │ ├── queries │ └── query_template.md │ └── views │ └── view_template.md ├── LICENSE ├── README.md ├── facebook-ads ├── queries │ ├── action_insights_by_ad_format_and_optimization_type.md │ ├── campaign_costs_per_month.md │ ├── last_7_vs_previous_7.md │ ├── monthly_breakdown.md │ ├── target_audiences_by_optimization_goal.md │ └── total_value_by_ad_quality.md └── views │ ├── ad_accounts.md │ ├── ads.md │ ├── ads_adcreatives.md │ ├── adsets.md │ ├── adsets_adcreatives.md │ ├── adsets_targeting.md │ ├── adsets_targeting_custom_audiences.md │ ├── campaigns.md │ ├── insights.md │ ├── insights_actions.md │ ├── insights_outbound_clicks.md │ └── insights_video_views.md ├── google-adwords └── queries │ └── monthly_breakdown.md ├── google-analytics └── queries │ ├── last_7_vs_previous_7.md │ └── medium_breakdown.md ├── hubspot ├── queries │ ├── average-days-to-close--by-close-month.md │ ├── average-engagements-to-close-by-deal-type.md │ ├── contact-to-mql.md │ ├── expected-revenue-by-close-month.md │ ├── expected-revenue-by-hubspot-owner.md │ ├── form-submissions-analysis.md │ ├── lifetime-value-and-lifetime-deal-count.md │ ├── market-cap-companies.md │ ├── number-of-contacts-by-city.md │ └── number-of-contacts-by-source.md └── views │ ├── companies.md │ ├── companies_consolidated.md │ ├── contacts.md │ ├── contacts_consolidated.md │ ├── deals_companies.md │ ├── deals_consolidated.md │ ├── deals_contacts.md │ ├── engagements_consolidated.md │ └── owners_consolidated.md ├── salesforce ├── queries │ ├── new_opps_summary.md │ ├── revenue_and_loss_by_opportunity_type.md │ ├── sales_rep_conversion_rate_qtd.md │ └── sales_rep_ranked_by_revenue.md └── views │ └── account_to_email.md ├── shopify └── queries │ ├── customer_order_annual_overview.md │ └── order_count_by_processing_method.md └── zendesk └── queries └── ticket_count_per_status_by_paying_customer.md /.github/data-source-template/queries/query_template.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: Short but Descriptive Title Without the Source Name 3 | description: Brief summary of the query 4 | usage: When and why to use this query 5 | modifications: List ways this query could be modified for custom use 6 | --- 7 | 8 | ```sql 9 | SELECT top 1 value AS my_value 10 | FROM table_name 11 | ORDER BY value DESC; 12 | ``` 13 | 14 | ## Query Results Dictionary 15 | | Column | Description | 16 | | --- | --- | 17 | | `my_field`| Brief description of the column | 18 | | `my_other_field`| Brief description of the column | 19 | -------------------------------------------------------------------------------- /.github/data-source-template/views/view_template.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: Short but Descriptive Title Without the Source Name 3 | description: Brief summary of the view 4 | usage: When and why to use this view 5 | modifications: List ways this view could be modified for custom use 6 | --- 7 | 8 | ```sql 9 | -- CREATE VIEW view_name AS 10 | SELECT * 11 | FROM some_table t 12 | JOIN some_other_table ot ON t.email=ot.email 13 | ``` 14 | 15 | ## Example: {Name of example} 16 | 17 | ```sql 18 | SELECT * 19 | FROM view_name 20 | WHERE email not ilike '%gmail%'; 21 | ``` 22 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2019 Panoply 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # sql-library 2 | A collection of useful SQL queries and view definitions for Panoply Data Sources 3 | 4 | ## Directory structure 5 | Every Panoply data source should have its own directory in the top level of the repo. Each data source directory should have two subdirectories: `views` and `queries`. 6 | 7 | For example: 8 | ``` 9 | / 10 | /salesforce 11 | /views 12 | /queries 13 | /shopify 14 | /views 15 | /queries 16 | /... 17 | ``` 18 | 19 | ### `/views` 20 | `views` are queries that perform significant ELT transformations and create tables to facilitate analysis and reporting. This will often be used to join and normalize tables that were created from nested JSON or create roll-up reporting tables that are easy for non-technical BI tool users to use with their BI tool of choice. Simply put: `views` are building blocks for queries. 21 | 22 | ### `/queries` 23 | `queries` demonstrate how you can query your data in Panoply. Most `queries` will probably fall into one of two buckets: "metrics," calculations like Customer Lifetime Value that can be further segmented out; and questions, like "What is the best day of the week to post on Instagram?” 24 | 25 | ## Markdown file templates 26 | Each Panoply data source will have its own directory modeled after the `.github/data-source-template` directory. Within that directory, there are two subdirectories: `/views` and `/queries`, each of these contains a corresponding template markdown file. 27 | 28 | The main differences between the two templates are: `views` files have the `CREATE VIEW` syntax commented out. The `CREATE VIEW` syntax is not necessary if saving the view in Panoply. The line can also be uncommented when saving the view in other SQL workbenches. `views` files also contain an example query that could be used with the view definition. Finally, `queries` files contain a data dictionary of the columns that the query returns. 29 | -------------------------------------------------------------------------------- /facebook-ads/queries/action_insights_by_ad_format_and_optimization_type.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: Action Insights by Ad Format and Optimization Type 3 | description: This query aggregates actions metrics per the format and optimization type of each ad 4 | usage: This query can be used to create an histogram of multiple metrics for ads' formats and optimization types 5 | --- 6 | 7 | ```sql 8 | SELECT 9 | ac."status", 10 | ac.ad_format, 11 | ac.optimization_type, 12 | sum(ia.action_count) AS total_actions, 13 | sum(ia.action_value) AS total_value_of_actions, 14 | sum(ia.cost_per_action) AS total_cost_of_actions, 15 | avg(ia.action_count) AS avg_action_count, 16 | avg(ia.action_value) AS avg_value_per_action, 17 | avg(ia.cost_per_action) AS avg_cost_per_action, 18 | round( 19 | 1.0 * sum(iv.avg_time_watched) / nullif(count(iv.video_action_id), 0), 20 | 2 21 | ) as avg_time_watched_per_ad_with_video, 22 | round( 23 | 1.0 * sum(iv."100_percent_watched_count") / nullif(count(i.insights_id), 0), 24 | 2 25 | ) as fully_watched_video_percent 26 | FROM 27 | fb_ads_ads_adcreatives ac 28 | LEFT JOIN fb_ads_insights i ON i.ad_id = ac.ad_id 29 | LEFT JOIN fb_ads_insights_actions ia ON ia.insights_id = i.insights_id 30 | LEFT JOIN fb_ads_insights_video_views iv on i.insights_id = iv.insights_id 31 | GROUP BY 1, 2, 3; 32 | ``` 33 | 34 | ## Query Results Dictionary 35 | 36 | | Column | Description | 37 | | --- | --- | 38 | | `status`| The ad creative status | 39 | | `ad_format`| The ad format | 40 | | `optimization_type`| The ad's optimization type | 41 | | `total_actions`| Total number of actions | 42 | | `total_value_of_actions`| Total value of all actions | 43 | | `total_cost_of_actions`| Total cost of all actions | 44 | | `avg_action_count`| Average number of actions | 45 | | `avg_value_per_action`| Average value per action | 46 | | `avg_cost_per_action`| Average cost per action | 47 | -------------------------------------------------------------------------------- /facebook-ads/queries/campaign_costs_per_month.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: Campaigns Metrics per Month 3 | description: This query aggregates multiple metrics per month for all the campaigns. 4 | usage: This query can be used to create line charts per metric or a high level overview of the Facebook Ads' campaigns per month. 5 | --- 6 | 7 | ```sql 8 | select 9 | date_part(YEAR, c.start_time) AS "Ad Start Year", 10 | date_part(MONTH, c.start_time) AS "Ad Start Month", 11 | sum(i.clicks) as monthly_clicks, 12 | sum(i.unique_clicks) as monthly_unique_clicks, 13 | avg(i.clicks) as avg_clicks_per_ad, 14 | round( 15 | 1.0 * sum(i.clicks) / nullif(sum(unique_clicks), 0), 16 | 4 17 | ) as clicks_per_user, 18 | round(sum(i.cpc * i.clicks), 4) as total_cost, 19 | round(avg(i.cpc), 4) as avg_cost_per_click, 20 | round(avg(i.cpm), 4) as avg_cost_per_1000_impressions, 21 | round(avg(i.cpp), 4) as avg_cost_per_purchase, 22 | avg(i.ctr) as avg_ctr, 23 | avg(i.unique_ctr) as avg_unique_ctr 24 | from 25 | fb_ads_campaigns c 26 | left join fb_ads_insights i on c.campaign_id = i.campaign_id 27 | group by 1, 2 28 | order by 1, 2; 29 | ``` 30 | 31 | ## Query Results Dictionary 32 | 33 | | Column | Description | 34 | | --- | --- | 35 | | `Ad Start Year`| Year of aggregation. | 36 | | `Ad Start Month`| Month of aggregation. | 37 | | `monthly_clicks`| Total monthly clicks. | 38 | | `monthly_unique_clicks`| Total monthly unique clicks. | 39 | | `avg_clicks_per_ad`| Average clicks per ad. | 40 | | `clicks_per_user`| Average clicks per user. | 41 | | `total_cost`| total cost. | 42 | | `avg_cost_per_click`| Average cost per click per ad. | 43 | | `avg_cost_per_1000_impressions`| Average cost per 1,000 impressions per ad. | 44 | | `avg_cost_per_purchase`| Average cost per purchase (Facebook pixel) per ad. | 45 | | `avg_ctr`| Average click-through rate. | 46 | | `avg_unique_ctr`| Average unique click-through rate. | 47 | -------------------------------------------------------------------------------- /facebook-ads/queries/last_7_vs_previous_7.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: Last 7 Days VS Previous 7 Days 3 | description: This query compares the sessions of the last 7 days to the previous 7 days aggregated by `campaign_name`. This query demonstrates the use of a concept that can be used with any dimension and metric\\s, comparing different values of the metric based on different values of time or a given dimension. 4 | requirements: Collect data with the Panoply Facebook Ads data source. 5 | usage: This query can be used to create a bar chart comparing the `last_7` and `previous_7` for each campaign or simply displayed as a table. 6 | modifications: The table in the `FROM` might need to be changed based on Schema and Destination settings in the data source. Other dimensions and metrics can be added as filters to the `WHERE` or by adding a `HAVING` clause and other aggregations can be added on top of `last_7` and `previous_7`. This query sums the metric `clicks`, this can be changed to a different metric like `spend` or `impressions`. 7 | --- 8 | 9 | ```sql 10 | SELECT 11 | campaign_name, 12 | SUM( 13 | CASE WHEN date_start >= current_date - 7 14 | AND date_start < current_date THEN clicks END 15 | ) last_7, 16 | SUM( 17 | CASE WHEN date_start >= current_date - 14 18 | AND date_start < current_date - 7 THEN clicks END 19 | ) previous_7 20 | FROM 21 | public."facebook-ads" -- Table name might be different based on Schema and Destination settings in the data source 22 | WHERE 23 | date_start >= current_date - 14 24 | AND date_start < current_date 25 | GROUP BY 26 | 1 27 | ORDER BY 28 | 1 29 | ``` 30 | 31 | ## Query Results Dictionary 32 | 33 | | Column | Description | 34 | | --- | --- | 35 | | `campaign_name`| Facebook Ads campaign name | 36 | | `last_7`| Values for last 7 days | 37 | | `previous_7`| Values for previous 7 days | 38 | -------------------------------------------------------------------------------- /facebook-ads/queries/monthly_breakdown.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: Monthly Performance Breakdown 3 | description: This query sums the total cost, clicks and impressions by month and year from `facebook-ads`. 4 | requirements: Collect data with the Panoply Facebook Ads data source. 5 | usage: This query can be used to create a line or bar chart of cost \ impressions \ clicks over time. 6 | modifications: The table in the `FROM` might need to be changed based on Schema and Destination settings in the data source. The columns `campaign_name` or your chosen breakdown (`country` by default) can be added to the `WHERE` as filters or to the `SELECT` and `GROUP BY` for an additional level of aggregation granularity. 7 | --- 8 | 9 | ```sql 10 | SELECT 11 | DATE_TRUNC('month', date_start) :: date year_month, 12 | ROUND(SUM(spend), 2) cost, 13 | SUM(clicks) clicks, 14 | SUM(impressions) impressions, 15 | ROUND(SUM(clicks :: float)/ SUM(impressions), 3) clickthrough_rate, 16 | ROUND(SUM(spend :: float)/ SUM(clicks), 2) cost_per_click 17 | FROM 18 | public."facebook-ads" -- Table name might be different based on Schema and Destination settings in the data source 19 | GROUP BY 20 | 1 21 | ORDER BY 22 | 1 ASC 23 | ``` 24 | 25 | ## Query Results Dictionary 26 | 27 | | Column | Description | 28 | | --- | --- | 29 | | `year_month`| Year and month extracted from the date_start column. Values are in date format with the first day of each month to represent that given month. | 30 | | `cost`| Total monthly cost in the ad account's chosen currency | 31 | | `clicks`| Total monthly clicks | 32 | | `impressions`| Total monthly impressions | 33 | | `clickthrough_rate`| Monthly clicks divided by monthly impressions | 34 | | `cost_per_click`| Total monthly cost divided by monthly clicks | 35 | -------------------------------------------------------------------------------- /facebook-ads/queries/target_audiences_by_optimization_goal.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: Target Audiences by Optimization Goal 3 | description: This query counts the number of adsets targeting each audience per optimization goal. 4 | usage: This query can be used to create an histogram of number of adsets in each audience per optimization goal. 5 | modifications: Add or change target audiences. 6 | --- 7 | 8 | ```sql 9 | select 10 | "as".optimization_goal 11 | , sum(case when t.target_gender = 'female' then 1 else null end) as female_targets 12 | , sum(case when t.target_device_platforms like 'mobile%' then 1 else null end) as mobile_device_targets 13 | , sum(case when t.target_device_platforms like 'desktop%' then 1 else null end) as desktop_targets 14 | , sum(case when t.publisher_platforms = 'instagram' then 1 else null end) as instagram_targets 15 | , sum(case when t.targeting_optimization = 'expansion_all' then 1 else null end) as expansion_all_target_optimization 16 | from 17 | fb_ads_adsets "as" 18 | left join fb_ads_adsets_targeting t on "as".adset_id=t.adset_id 19 | group by 1; 20 | ``` 21 | 22 | ## Query Results Dictionary 23 | 24 | | Column | Description | 25 | | --- | --- | 26 | | `optimization_goal`| Optimization goal of the adsets. | 27 | | `female_targets`| Number of adsets that target females. | 28 | | `mobile_device_targets`| Number of adsets that target mobile devices. | 29 | | `desktop_targets`| Number of adsets that target desktop devices. | 30 | | `instagram_targets`| Number of adsets that target Instagram users. | 31 | | `expansion_all_target_optimization`| Number of adsets that uses targeting expansion. | 32 | -------------------------------------------------------------------------------- /facebook-ads/queries/total_value_by_ad_quality.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: Total Value by Ad Quality 3 | description: This query aggregates multiple metrics for each adset. 4 | usage: This query can be used to see main KPIs for adsets. The KPIs include but not limited to quality informaion, impressions, reach, spend, etc. 5 | --- 6 | 7 | ```sql 8 | select 9 | i.account_name, 10 | i.campaign_name, 11 | i.adset_name, 12 | sum(i.spend) as total_spend, 13 | sum(i.impressions) as total_impressions, 14 | sum(i.reach) as total_reach, 15 | sum(case when i.quality_ranking='BELOW_AVERAGE_10' then ia.action_value else null end) as quality_below_average_10_value 16 | ,sum(case when i.quality_ranking='BELOW_AVERAGE_20' then ia.action_value else null end) as quality_below_average_20_value 17 | ,sum(case when i.quality_ranking='BELOW_AVERAGE_35' then ia.action_value else null end) as quality_below_average_35_value 18 | ,sum(case when i.quality_ranking='AVERAGE' then ia.action_value else null end) as quality_below_average_value 19 | ,sum(case when i.quality_ranking='ABOVE_AVERAGE' then ia.action_value else null end) as quality_above_average_value 20 | ,sum(case when i.quality_ranking='UNKNOWN' then ia.action_value else null end) as quality_unknown_value 21 | ,sum(case when i.engagement_rate_ranking='BELOW_AVERAGE_10' then ia.action_value else null end) as engagement_rate_below_average_10_value 22 | ,sum(case when i.engagement_rate_ranking='BELOW_AVERAGE_20' then ia.action_value else null end) as engagement_rate_below_average_20_value 23 | ,sum(case when i.engagement_rate_ranking='BELOW_AVERAGE_35' then ia.action_value else null end) as engagement_rate_below_average_35_value 24 | ,sum(case when i.engagement_rate_ranking='AVERAGE' then ia.action_value else null end) as engagement_rate_below_average_value 25 | ,sum(case when i.engagement_rate_ranking='ABOVE_AVERAGE' then ia.action_value else null end) as engagement_rate_above_average_value 26 | ,sum(case when i.engagement_rate_ranking='UNKNOWN' then ia.action_value else null end) as engagement_rate_unknown_value 27 | ,sum(case when i.conversion_rate_ranking='BELOW_AVERAGE_10' then ia.action_value else null end) as conversion_rate_below_average_10_value 28 | ,sum(case when i.conversion_rate_ranking='BELOW_AVERAGE_20' then ia.action_value else null end) as conversion_rate_below_average_20_value 29 | ,sum(case when i.conversion_rate_ranking='BELOW_AVERAGE_35' then ia.action_value else null end) as conversion_rate_below_average_35_value 30 | ,sum(case when i.conversion_rate_ranking='AVERAGE' then ia.action_value else null end) as conversion_rate_below_average_value 31 | ,sum(case when i.conversion_rate_ranking='ABOVE_AVERAGE' then ia.action_value else null end) as conversion_rate_above_average_value 32 | ,sum(case when i.conversion_rate_ranking='UNKNOWN' then ia.action_value else null end) as conversion_rate_unknown_value 33 | from 34 | fb_ads_insights i 35 | left join fb_ads_insights_actions ia on i.insights_id = ia.insights_id 36 | group by 1, 2, 3; 37 | ``` 38 | 39 | ## Query Results Dictionary 40 | 41 | | Column | Description | 42 | | --- | --- | 43 | | `account_name`| Ad account name. | 44 | | `campaign_name`| The name of the campaign . | 45 | | `adset_name`| The name of the adset. | 46 | | `total_spend`| Total spend for adset. | 47 | | `total_impressions`| Total impressions for adset. | 48 | | `total_reach`| Total reach of adset. | 49 | | `quality_below_average_10_value`| Total value for quality below average 10. | 50 | | `quality_below_average_20_value`| Total value for quality below average 20. | 51 | | `quality_below_average_35_value`| Total value for quality below average 35. | 52 | | `quality_below_average_value`| Total value for quality below average. | 53 | | `quality_above_average_value`| Total value for quality above average. | 54 | | `quality_unknown_value`| Total value for quality unknown. | 55 | | `engagement_rate_below_average_10_value`| Total value for engagement rate below average 10. | 56 | | `engagement_rate_below_average_20_value`| Total value for engagement rate below average 20. | 57 | | `engagement_rate_below_average_35_value`| Total value for engagement rate below average 35. | 58 | | `engagement_rate_below_average_value`| Total value for engagement rate below average. | 59 | | `engagement_rate_above_average_value`| Total value for engagement rate above average. | 60 | | `engagement_rate_unknown_value`| Total value for engagement rate unknown. | 61 | | `conversion_rate_below_average_10_value`| Total value for conversion rate below average 10. | 62 | | `conversion_rate_below_average_20_value`| Total value for conversion rate below average 20. | 63 | | `conversion_rate_below_average_35_value`| Total value for conversion rate below average 35. | 64 | | `conversion_rate_below_average_value`| Total value for conversion rate below average. | 65 | | `conversion_rate_above_average_value`| Total value for conversion rate above average. | 66 | | `conversion_rate_unknown_value`| Total value for conversion rate unknown. | 67 | -------------------------------------------------------------------------------- /facebook-ads/views/ad_accounts.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: Facebook Ad Accounts 3 | description: Focused version of the ad accounts data 4 | usage: This is the "ready for analysis" view of Facebook Ads' ad account information 5 | modifications: Properties can be removed or added. 6 | --- 7 | 8 | ```sql 9 | -- CREATE VIEW fb_ads_ad_account AS 10 | select 11 | account_id, 12 | name, 13 | account_status, 14 | is_personal, 15 | age, 16 | business_country_code, 17 | fb_entity, 18 | spend_cap, 19 | balance, 20 | amount_spent, 21 | currency, 22 | funding_source, 23 | min_daily_budget, 24 | min_campaign_group_spend_cap, 25 | is_prepay_account, 26 | timezone_id, 27 | timezone_name, 28 | timezone_offset_hours_utc, 29 | tax_id_status, 30 | is_tax_id_required, 31 | has_migrated_permissions, 32 | is_in_middle_of_local_entity_migration, 33 | is_notifications_enabled, 34 | can_create_brand_lift_study, 35 | offsite_pixels_tos_accepted, 36 | is_attribution_spec_system_default, 37 | is_in_3ds_authorization_enabled_market, 38 | is_direct_deals_enabled, 39 | created_time 40 | from 41 | facebook_ads_ad_accounts; 42 | ``` 43 | -------------------------------------------------------------------------------- /facebook-ads/views/ads.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: Facebook Ads 3 | description: Focused version of the ads data 4 | usage: This is the "ready for analysis" view of Facebook Ads' ads information 5 | modifications: Properties can be removed or added. 6 | --- 7 | 8 | ```sql 9 | -- CREATE VIEW fb_ads_ads AS 10 | SELECT id AS "ad_id" 11 | , source_ad_id 12 | , account_id 13 | , campaign_id 14 | , adset_id 15 | , "name" 16 | , preview_shareable_link 17 | , "STATUS" 18 | , effective_status 19 | , last_updated_by_app_id 20 | , created_time 21 | , updated_time 22 | FROM facebook_ads_ads; 23 | ``` 24 | -------------------------------------------------------------------------------- /facebook-ads/views/ads_adcreatives.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: Facebook Ads Ad Creatives 3 | description: Focused version of the ad creatives data 4 | usage: This is the "ready for analysis" view of Facebook Ads' ad creatives information 5 | modifications: Properties can be removed or added. 6 | --- 7 | 8 | ```sql 9 | -- CREATE VIEW fb_ads_ads_adcreatives AS 10 | SELECT aac.id AS "ads_adcreatives_id" 11 | , aac.facebook_ads_ads_id AS "ad_id" 12 | , aac.account_id 13 | , aac.name 14 | , CASE WHEN aac.title IS NULL THEN afs_t.TEXT ELSE aac.title END AS "title" 15 | , CASE WHEN aac."body" IS NULL THEN afs_b.TEXT ELSE aac.body END AS "text_body" 16 | , afs_d."text" AS "description" 17 | , aac.STATUS 18 | , aac.object_type 19 | , CASE WHEN aac.call_to_action_type IS NULL THEN afs_cta."value" ELSE aac.call_to_action_type END AS "call_to_action_type" 20 | , aac.authorization_category 21 | , aac.effective_authorization_category 22 | , afs.optimization_type 23 | , afs_af."value" AS "ad_format" 24 | , afs_lu.website_url AS "website_url" 25 | , aac.instagram_permalink_url 26 | , aac.thumbnail_url 27 | , aac.image_url 28 | , aac.image_hash 29 | , aac.url_tags 30 | , aac.use_page_actor_override 31 | , aac.enable_direct_install 32 | , aac.enable_launch_instant_app 33 | FROM facebook_ads_ads_adcreatives aac 34 | LEFT JOIN facebook_ads_ads_adcreatives_asset_feed_spec afs ON aac.id = afs.facebook_ads_ads_adcreatives_id 35 | LEFT JOIN facebook_ads_ads_adcreatives_asset_feed_spec_ad_formats afs_af ON afs.id = afs_af.facebook_ads_ads_adcreatives_asset_feed_spec_id 36 | LEFT JOIN facebook_ads_ads_adcreatives_asset_feed_spec_titles afs_t ON afs.id = afs_t.facebook_ads_ads_adcreatives_asset_feed_spec_id 37 | LEFT JOIN facebook_ads_ads_adcreatives_asset_feed_spec_bodies afs_b ON afs.id = afs_b.facebook_ads_ads_adcreatives_asset_feed_spec_id 38 | LEFT JOIN facebook_ads_ads_adcreatives_asset_feed_spec_descriptions afs_d ON afs.id = afs_d.facebook_ads_ads_adcreatives_asset_feed_spec_id AND afs_d."text" IS NOT NULL 39 | LEFT JOIN facebook_ads_ads_adcreatives_asset_feed_spec_call_to_action_types afs_cta ON afs.id = afs_cta.facebook_ads_ads_adcreatives_asset_feed_spec_id 40 | LEFT JOIN ( 41 | SELECT DISTINCT facebook_ads_ads_adcreatives_asset_feed_spec_id 42 | , website_url 43 | FROM facebook_ads_ads_adcreatives_asset_feed_spec_link_urls 44 | ) afs_lu ON afs.id = afs_lu.facebook_ads_ads_adcreatives_asset_feed_spec_id; 45 | ``` 46 | -------------------------------------------------------------------------------- /facebook-ads/views/adsets.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: Facebook Adsests 3 | description: Focused version of the adsets data 4 | usage: This is the "ready for analysis" view of Facebook Ads' adsets information 5 | modifications: Properties can be removed or added. 6 | --- 7 | 8 | ```sql 9 | -- CREATE VIEW fb_ads_adsets AS 10 | SELECT 11 | id as "adset_id", 12 | source_adset_id, 13 | account_id, 14 | campaign_id, 15 | name, 16 | status, 17 | effective_status, 18 | lifetime_budget, 19 | budget_remaining, 20 | daily_budget, 21 | daily_min_spend_target, 22 | recurring_budget_semantics, 23 | bid_strategy, 24 | optimization_goal, 25 | optimization_sub_event, 26 | destination_type, 27 | billing_event, 28 | lifetime_imps, 29 | use_new_app_click, 30 | multi_optimization_goal_weight, 31 | is_dynamic_creative, 32 | start_time, 33 | end_time, 34 | updated_time, 35 | created_time 36 | FROM 37 | facebook_ads_adsets; 38 | ``` 39 | -------------------------------------------------------------------------------- /facebook-ads/views/adsets_adcreatives.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: Facebook Adsets Ad Creatives 3 | description: Focused version of the adsets ad creatives data 4 | usage: This is the "ready for analysis" view of Facebook Ads' adsets ad creatives information 5 | modifications: Properties can be removed or added. 6 | --- 7 | 8 | ```sql 9 | -- CREATE VIEW fb_ads_adsets_adcreatives AS 10 | SELECT aac.id AS "adsets_adcreatives_id" 11 | , aac.facebook_ads_adsets_id AS "adset_id" 12 | , aac.account_id 13 | , aac.name 14 | , CASE WHEN aac.title IS NULL THEN afs_t.TEXT ELSE aac.title END AS "title" 15 | , CASE WHEN aac."body" IS NULL THEN afs_b.TEXT ELSE aac.body END AS "text_body" 16 | , afs_d."text" AS "description" 17 | , aac.STATUS 18 | , aac.object_type 19 | , CASE WHEN aac.call_to_action_type IS NULL THEN afs_cta."value" ELSE aac.call_to_action_type END AS "call_to_action_type" 20 | , aac.authorization_category 21 | , aac.effective_authorization_category 22 | , afs.optimization_type 23 | , afs_af."value" AS "ad_format" 24 | , afs_lu.website_url AS "website_url" 25 | , aac.instagram_permalink_url 26 | , aac.thumbnail_url 27 | , aac.image_url 28 | , aac.image_hash 29 | , aac.url_tags 30 | FROM facebook_ads_adsets_adcreatives aac 31 | LEFT JOIN facebook_ads_adsets_adcreatives_asset_feed_spec afs ON aac.id = afs.facebook_ads_adsets_adcreatives_id 32 | LEFT JOIN facebook_ads_adsets_adcreatives_asset_feed_spec_ad_formats afs_af ON afs.id = afs_af.facebook_ads_adsets_adcreatives_asset_feed_spec_id 33 | LEFT JOIN facebook_ads_adsets_adcreatives_asset_feed_spec_titles afs_t ON afs.id = afs_t.facebook_ads_adsets_adcreatives_asset_feed_spec_id 34 | LEFT JOIN facebook_ads_adsets_adcreatives_asset_feed_spec_bodies afs_b ON afs.id = afs_b.facebook_ads_adsets_adcreatives_asset_feed_spec_id 35 | LEFT JOIN facebook_ads_adsets_adcreatives_asset_feed_spec_descriptions afs_d ON afs.id = afs_d.facebook_ads_adsets_adcreatives_asset_feed_spec_id AND afs_d."text" IS NOT NULL 36 | LEFT JOIN facebook_ads_adsets_adcreatives_asset_feed_spec_call_to_action_types afs_cta ON afs.id = afs_cta.facebook_ads_adsets_adcreatives_asset_feed_spec_id 37 | LEFT JOIN ( 38 | SELECT DISTINCT facebook_ads_adsets_adcreatives_asset_feed_spec_id 39 | , website_url 40 | FROM facebook_ads_adsets_adcreatives_asset_feed_spec_link_urls 41 | ) afs_lu ON afs.id = afs_lu.facebook_ads_adsets_adcreatives_asset_feed_spec_id; 42 | ``` 43 | -------------------------------------------------------------------------------- /facebook-ads/views/adsets_targeting.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: Facebook Adsets Targeting 3 | description: Focused version of the adsets targeting data 4 | usage: This is the "ready for analysis" view of Facebook Ads' adsets targeting information 5 | --- 6 | 7 | ```sql 8 | -- CREATE VIEW fb_ads_adsets_targeting AS 9 | select 10 | t.id as "targeting_id", 11 | t.facebook_ads_adsets_id as "adset_id", 12 | ex.id as "exclusions_id", 13 | geo.id as "geo_locations_id", 14 | t.age_max, 15 | t.age_min, 16 | t.targeting_optimization, 17 | case 18 | tg."value" 19 | when 2 then 'female' 20 | when 1 then 'male' 21 | else null 22 | end as "target_gender", 23 | pp."value" as "publisher_platforms", 24 | igp.instagram_positions as "instagram_positions", 25 | dv.device as "target_device_platforms" 26 | from 27 | facebook_ads_adsets_targeting t 28 | left join facebook_ads_adsets_targeting_exclusions ex on t.id = ex.facebook_ads_adsets_targeting_id 29 | left join facebook_ads_adsets_targeting_genders tg on t.id = tg.facebook_ads_adsets_targeting_id 30 | left join facebook_ads_adsets_targeting_geo_locations geo on t.id = geo.facebook_ads_adsets_targeting_id 31 | left join facebook_ads_adsets_targeting_publisher_platforms pp on t.id = pp.facebook_ads_adsets_targeting_id 32 | left join ( 33 | select 34 | facebook_ads_adsets_targeting_id, 35 | listagg(value, '; ') within group ( 36 | ORDER BY 37 | value 38 | ) as instagram_positions 39 | from 40 | facebook_ads_adsets_targeting_instagram_positions 41 | group by 42 | 1 43 | ) igp on t.id = igp.facebook_ads_adsets_targeting_id 44 | left join ( 45 | select 46 | facebook_ads_adsets_targeting_id, 47 | listagg (value, '; ') within group ( 48 | order by 49 | value 50 | ) as device 51 | from 52 | facebook_ads_adsets_targeting_device_platforms 53 | group by 54 | 1 55 | ) dv on t.id = dv.facebook_ads_adsets_targeting_id; 56 | ``` 57 | -------------------------------------------------------------------------------- /facebook-ads/views/adsets_targeting_custom_audiences.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: Facebook Adsets Targeting Custom Audiences 3 | description: Focused version of the adsets targeting data for custom audiences only 4 | usage: This is the "ready for analysis" view of Facebook Ads' adsets targeting for custom audiences information 5 | --- 6 | 7 | ```sql 8 | -- CREATE VIEW fb_adsets_targeting_custom_audiences AS 9 | select 10 | ca.id || '_ca' as "custom_audience_id", 11 | ca.facebook_ads_adsets_targeting_id as "targeting_id", 12 | ca."name" as "audience_name", 13 | null as "excluded" 14 | from 15 | facebook_ads_adsets_targeting_custom_audiences ca 16 | union 17 | select 18 | eca.id || '_eca' as "custom_audience_id", 19 | eca.facebook_ads_adsets_targeting_id as "targeting_id", 20 | eca."name" as "audience_name", 21 | True as "excluded" 22 | from 23 | facebook_ads_adsets_targeting_excluded_custom_audiences eca; 24 | ``` 25 | -------------------------------------------------------------------------------- /facebook-ads/views/campaigns.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: Facebook Campaigns 3 | description: Focused version of the campaigns data 4 | usage: This is the "ready for analysis" view of Facebook Ads' campaigns information 5 | modifications: Properties can be removed or added. 6 | --- 7 | 8 | ```sql 9 | -- CREATE VIEW fb_ads_campaigns AS 10 | SELECT 11 | c.id as "campaign_id", 12 | c.source_campaign_id, 13 | c.account_id, 14 | c.name, 15 | c.status, 16 | c.effective_status, 17 | c.special_ad_category, 18 | c.lifetime_budget, 19 | c.budget_remaining, 20 | c.daily_budget, 21 | c.last_budget_toggling_time, 22 | c.spend_cap, 23 | c.objective, 24 | c.bid_strategy, 25 | c.buying_type, 26 | cpt."value" as "pacing_type", 27 | c.can_use_spend_cap, 28 | c.can_create_brand_lift_study, 29 | c.start_time, 30 | c.stop_time, 31 | c.created_time, 32 | c.updated_time 33 | FROM 34 | facebook_ads_campaigns c 35 | LEFT JOIN facebook_ads_campaigns_pacing_type cpt on c.id = cpt.facebook_ads_campaigns_id; 36 | ``` 37 | -------------------------------------------------------------------------------- /facebook-ads/views/insights.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: Facebook Insights 3 | description: Focused version of the insights data 4 | usage: This is the "ready for analysis" view of Facebook Ads' insights information 5 | modifications: Properties can be removed or added. 6 | --- 7 | 8 | ```sql 9 | -- CREATE VIEW fb_ads_insights AS 10 | SELECT 11 | id as "insights_id", 12 | account_id, 13 | campaign_id, 14 | adset_id, 15 | ad_id, 16 | account_name, 17 | campaign_name, 18 | adset_name, 19 | ad_name, 20 | objective, 21 | spend, 22 | social_spend, 23 | account_currency, 24 | buying_type, 25 | frequency, 26 | reach, 27 | impressions, 28 | quality_ranking, 29 | engagement_rate_ranking, 30 | conversion_rate_ranking, 31 | clicks, 32 | cpc, 33 | cpm, 34 | cpp, 35 | ctr, 36 | unique_clicks, 37 | cost_per_unique_click, 38 | unique_ctr, 39 | unique_link_clicks_ctr, 40 | inline_link_clicks, 41 | cost_per_inline_link_click, 42 | inline_link_click_ctr, 43 | unique_inline_link_clicks, 44 | cost_per_unique_inline_link_click, 45 | unique_inline_link_click_ctr, 46 | inline_post_engagement, 47 | cost_per_inline_post_engagement, 48 | date_start, 49 | date_stop 50 | FROM 51 | facebook_ads_insights; 52 | ``` 53 | -------------------------------------------------------------------------------- /facebook-ads/views/insights_actions.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: Facebook Insights Actions 3 | description: Focused version of the insights actions data 4 | usage: This is the "ready for analysis" view of Facebook Ads' insights actions information 5 | --- 6 | 7 | ```sql 8 | -- CREATE VIEW fb_ads_insights_actions AS 9 | select 10 | a.id as "action_id", 11 | a.facebook_ads_insights_id as "insights_id", 12 | a.action_type, 13 | a."value" as action_count, 14 | av."value" as action_value, 15 | cp_a."value" as "cost_per_action", 16 | ua."value" as "unique_action_count", 17 | cp_ua."value" as "cost_per_unique_action", 18 | 19 | wc.action_type as "website_ctr_action_type", 20 | wc."value" as "website_ctr", 21 | wpr.action_type as "website_purchase_roas_action_type", 22 | wpr."value" as "website_purchase_roas", 23 | 24 | a."1d_view" as "1d_view_count", 25 | av."1d_view" as "1d_view_value", 26 | cp_a."1d_view" as "cost_per_1d_view", 27 | ua."1d_view" as "unqiue_1d_view_count", 28 | cp_ua."1d_view" as "cost_per_unique_action_1d_view", 29 | wpr."1d_view" as "website_purchase_roas_1d_view", 30 | 31 | a."28d_click" as "28d_click_count", 32 | av."28d_click" as "28d_click_value", 33 | cp_a."28d_click" as "cost_per_28d_click", 34 | ua."28d_click" as "unique_28d_click_count", 35 | cp_ua."28d_click" as "cost_per_unique_action_28d_click", 36 | wpr."28d_click" as "website_purchase_roas_28d_click" 37 | from 38 | facebook_ads_insights_actions a 39 | left join facebook_ads_insights_action_values av using (id) 40 | left join facebook_ads_insights_unique_actions ua using(id) 41 | left join facebook_ads_insights_cost_per_action_type cp_a using (id) 42 | left join facebook_ads_insights_cost_per_unique_action_type cp_ua using (id) 43 | left join facebook_ads_insights_website_ctr wc using (id) 44 | left join facebook_ads_insights_website_purchase_roas wpr using (id); 45 | ``` 46 | -------------------------------------------------------------------------------- /facebook-ads/views/insights_outbound_clicks.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: Facebook Insights Outbound Clicks 3 | description: Focused version of the insights outbound clicks data 4 | usage: This is the "ready for analysis" view of Facebook Ads' insights outbound clicks information 5 | --- 6 | 7 | ```sql 8 | -- CREATE VIEW fb_ads_insights_outbound_clicks AS 9 | select 10 | oc.id as "outbound_click_id", 11 | oc.action_type as "action_type", -- will always be "outbound_click" in these tables. Used for joining to general action tables 12 | oc.facebook_ads_insights_id as "insights_id", 13 | oc."value" as "outbound_clicks_count", 14 | oc_ctr."value" as "outbound_clicks_ctr", 15 | cp_oc."value" as "cost_per_outbound_click", 16 | uoc."value" as "unique_outbound_clicks_count", 17 | uoc_ctr."value" as "unique_outbound_clicks_ctr", 18 | cp_uoc."value" as "cost_per_unique_outbound_click", 19 | oc."28d_click" as "outbound_28d_clicks_count", 20 | oc_ctr."28d_click" as "outbound_28d_clicks_ctr", 21 | cp_oc."28d_click" as "cost_per_outbound_28d_click", 22 | uoc."28d_click" as "unique_outbound_28d_clicks_count", 23 | uoc_ctr."28d_click" as "unique_outbound_28d_clicks_ctr", 24 | cp_uoc."28d_click" as "cost_per_unique_outbound_28d_click" 25 | from 26 | facebook_ads_insights_outbound_clicks oc 27 | inner join facebook_ads_insights_outbound_clicks_ctr oc_ctr using (id) 28 | inner join facebook_ads_insights_cost_per_outbound_click cp_oc using (id) 29 | inner join facebook_ads_insights_unique_outbound_clicks uoc using (id) 30 | inner join facebook_ads_insights_unique_outbound_clicks_ctr uoc_ctr using (id) 31 | inner join facebook_ads_insights_cost_per_unique_outbound_click cp_uoc using (id); 32 | ``` 33 | -------------------------------------------------------------------------------- /facebook-ads/views/insights_video_views.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: Facebook Insights Video Views 3 | description: Focused version of the insights video views data 4 | usage: This is the "ready for analysis" view of Facebook Ads' insights video views information 5 | --- 6 | 7 | ```sql 8 | -- CREATE VIEW fb_ads_insights_video_views AS 9 | select 10 | nvl( 11 | atwa.id, 12 | wa_30s.id, 13 | p25.id, 14 | p50.id, 15 | p75.id, 16 | p95.id, 17 | p100.id 18 | ) as "video_action_id", 19 | nvl( 20 | atwa.facebook_ads_insights_id, 21 | wa_30s.facebook_ads_insights_id, 22 | p25.facebook_ads_insights_id, 23 | p50.facebook_ads_insights_id, 24 | p75.facebook_ads_insights_id, 25 | p95.facebook_ads_insights_id, 26 | p100.facebook_ads_insights_id 27 | ) as "insights_id", 28 | nvl( 29 | atwa.action_type, 30 | wa_30s.action_type, 31 | p25.action_type, 32 | p50.action_type, 33 | p75.action_type, 34 | p95.action_type, 35 | p100.action_type 36 | ) as "action_type", 37 | atwa."value" as "avg_time_watched", 38 | atwa."1d_view" as "avg_time_watched_1d_view", 39 | atwa."28d_click" as "avg_time_watched_28d_click", 40 | wa_30s."value" as "30_seconds_watched_count", 41 | wa_30s."1d_view" as "30_seconds_watched_count_1d_view", 42 | wa_30s."28d_click" as "30_seconds_watched_count_28d_click", 43 | p25."value" as "25_percent_watched_count", 44 | p25."1d_view" as "25_percent_watched_count_1d_view", 45 | p25."28d_click" as "25_percent_watched_count_28d_click", 46 | p50."value" as "50_percent_watched_count", 47 | p50."1d_view" as "50_percent_watched_count_1d_view", 48 | p50."28d_click" as "50_percent_watched_count_28d_click", 49 | p75."value" as "75_percent_watched_count", 50 | p75."1d_view" as "75_percent_watched_count_1d_view", 51 | p75."28d_click" as "75_percent_watched_count_28d_click", 52 | p95."value" as "95_percent_watched_count", 53 | p95."1d_view" as "95_percent_watched_count_1d_view", 54 | p95."28d_click" as "95_percent_watched_count_28d_click", 55 | p100."value" as "100_percent_watched_count", 56 | p100."1d_view" as "100_percent_watched_count_1d_view", 57 | p100."28d_click" as "100_percent_watched_count_28d_click" 58 | from 59 | facebook_ads_insights_video_avg_time_watched_actions atwa 60 | full join facebook_ads_insights_video_30_sec_watched_actions wa_30s using (id) 61 | full join facebook_ads_insights_video_p25_watched_actions p25 using (id) 62 | full join facebook_ads_insights_video_p50_watched_actions p50 using (id) 63 | full join facebook_ads_insights_video_p75_watched_actions p75 using (id) 64 | full join facebook_ads_insights_video_p95_watched_actions p95 using (id) 65 | full join facebook_ads_insights_video_p100_watched_actions p100 using (id); 66 | ``` 67 | -------------------------------------------------------------------------------- /google-adwords/queries/monthly_breakdown.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: Monthly Performance Breakdown 3 | description: This query sums the total cost and conversions by month and year from the `adgroup_performance_report`. 4 | requirements: Collect the `Adgroup Performance Report` report with the Panoply Adwords API data source. 5 | usage: This query can be used to create a line or bar chart of cost and conversions over time. 6 | modifications: The table in the `FROM` might need to be changed based on Schema and Destination settings in the data source. The columns `ad group` or `campaign` can be added to the `WHERE` as filters or to the `SELECT` and `GROUP BY` for an additional level of aggregation granularity. 7 | --- 8 | 9 | ```sql 10 | SELECT 11 | DATE_TRUNC('month', "day") :: date year_month, 12 | ROUND(SUM(cost :: float / 1000000), 2) cost, -- divide cost by 1000000 to get Dollar since Google Provide Micro Dollar units - Link to Google Adwords Docs https://developers.google.com/adwords/api/docs/appendix/reports/adgroup-performance-report#cost 13 | SUM(conversions):: bigint conversions, 14 | SUM(clicks) clicks, 15 | SUM(impressions) impressions, 16 | SUM(clicks :: float)/ SUM(impressions) clickthrough_rate, 17 | SUM(cost :: float / 1000000)/ SUM(clicks) cost_per_click 18 | FROM 19 | public.adwords_adgroup_performance_report -- Table name might be different based on Schema and Destination settings in the data source 20 | GROUP BY 21 | 1 22 | ORDER BY 23 | 1 ASC 24 | ``` 25 | 26 | ## Query Results Dictionary 27 | 28 | | Column | Description | 29 | | --- | --- | 30 | | `year_month`| Year and month extracted from the day column. Values are in date format with the first day of each month to represent that given month. | 31 | | `cost`| Total monthly cost in Dollars | 32 | | `conversions`| Total monthly conversions | 33 | | `clicks`| Total monthly clicks | 34 | | `impressions`| Total monthly impressions | 35 | | `clickthrough_rate`| Monthly clicks divided by monthly impressions | 36 | | `cost_per_click`| Total monthly cost divided by monthly clicks | 37 | -------------------------------------------------------------------------------- /google-analytics/queries/last_7_vs_previous_7.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: Last 7 Days vs Previous 7 Days 3 | description: This query compares the sessions of the last 7 days to the previous 7 days aggregated by day. This query demonstrates the use of a concept that can be used with any dimension and metric\\s, comparing different values of the metric based on different values of the dimension. 4 | requirements: Collect any set of metrics and dimensions with at least one date or timestamp field with the Panoply Google Analytics data source. This example uses the `datehour` dimension for the date field but it could also use `date`, or `datehourminute`. This example also requires the `sessions` metric. 5 | usage: This query can be used to create a bar chart comparing the `last_7` and `previous_7` for each day of the week. 6 | modifications: The table in the `FROM` might need to be changed based on Schema and Destination settings in the data source. Other dimensions and metrics can be added to the `WHERE` as filters and other aggregations can be added on top of `last_7` and `previous_7`. 7 | --- 8 | 9 | ```sql 10 | SELECT 11 | TO_CHAR(datehour, 'Day') day_of_week, 12 | SUM( 13 | CASE WHEN datehour >= current_date - 7 14 | AND datehour < current_date THEN sessions END 15 | ) last_7, 16 | SUM( 17 | CASE WHEN datehour >= current_date - 14 18 | AND datehour < current_date - 7 THEN sessions END 19 | ) previous_7 20 | FROM 21 | public."google-analytics" -- Table name might be different based on Schema and Destination settings in the data source 22 | WHERE 23 | datehour >= current_date - 14 24 | AND datehour < current_date 25 | GROUP BY 26 | 1 27 | ORDER BY 28 | MIN( 29 | CASE WHEN datehour BETWEEN current_date - 7 30 | AND current_date - 1 THEN datehour END 31 | ) ASC 32 | ``` 33 | 34 | ## Query Results Dictionary 35 | 36 | | Column | Description | 37 | | --- | --- | 38 | | `day_of_week`| Day of the week extracted from datehour | 39 | | `last_7`| Values for last 7 days | 40 | | `previous_7`| Values for previous 7 days | 41 | -------------------------------------------------------------------------------- /google-analytics/queries/medium_breakdown.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: Medium Performance Breakdown 3 | description: This displays a sessions and bounce rate from the past 30 days aggregated by medium. 4 | requirements: Collect the Panoply Google Analytics data source with the default set of metrics and dimensions. This query uses the `medium` and `datehour` dimensions and the `sessions` and `bounces` metrics 5 | usage: The result of this query can be displayed as a table or also as a bar chart. 6 | modifications: The table in the `FROM` might need to be changed based on Schema and Destination settings in the data source. The columns `country` or `devicecategory` can be added to the `WHERE` as filters or to the `SELECT` and `GROUP BY` for an additional level of aggregation granularity. 7 | --- 8 | 9 | ```sql 10 | SELECT 11 | medium, 12 | SUM(sessions) sum_sessions, 13 | SUM(bounces):: float / SUM(sessions) bounce_rate 14 | FROM 15 | public."google-analytics" -- Table name might be different based on Schema and Destination settings in the data source 16 | WHERE 17 | datehour > sysdate - 30 -- Value can be changed \ Filter can be removed 18 | GROUP BY 19 | 1 20 | ORDER BY 21 | 2 DESC 22 | ``` 23 | 24 | ## Query Results Dictionary 25 | 26 | | Column | Description | 27 | | --- | --- | 28 | | `medium`| The type of referral | 29 | | `sum_sessions`| Sum of sessions | 30 | | `bounce_rate`| Sum of bounces divided by sum of sessions | 31 | -------------------------------------------------------------------------------- /hubspot/queries/average-days-to-close--by-close-month.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: Average Days to Close by Close Month 3 | description: This query calculates the average number of days to close (total, won and lost deals) per month. It does not include deals that were either closed won or closed lost. 4 | usage: This query can be used to create a line or bar chart of average days to close (three different metrics) per month. 5 | --- 6 | 7 | ```sql 8 | SELECT date_part(YEAR, close_date) AS "Close Year" 9 | , date_part(MONTH, close_date) AS "Close Month" 10 | , avg(days_to_close) AS "Average Days to Close" 11 | , avg(CASE WHEN date_entered_closedwon IS NOT NULL THEN cast(days_to_close AS INT) ELSE 0 END) AS "Average Days to Closed Won" 12 | , avg(CASE WHEN date_entered_closedlost IS NOT NULL THEN cast(days_to_close AS INT) ELSE 0 END) AS "Average Days to Closed Lost" 13 | FROM hubspot_deals_consolidated 14 | WHERE date_entered_closedwon IS NOT NULL OR date_entered_closedlost IS NOT NULL 15 | GROUP BY 1, 2 16 | ORDER BY 1, 2; 17 | ``` 18 | 19 | ## Query Results Dictionary 20 | 21 | | Column | Description | 22 | | --- | --- | 23 | | `Close Year`| Year of closing date. | 24 | | `Close Month`| Month of closing date. | 25 | | `Average Days to Close`| The average amount of days to close a deal. | 26 | | `Average Days to Closed Won`| The average amount of days to win a deal. | 27 | | `Average Days to Closed Lost`| The average amount of days to lose a deal. | 28 | -------------------------------------------------------------------------------- /hubspot/queries/average-engagements-to-close-by-deal-type.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: Average Engagements to Close by Deal Type 3 | description: This query calculates the average number of engagements for a closed deal per deal type. 4 | usage: This query can be used to create histogram of number of engagements in each deal type. 5 | --- 6 | 7 | ```sql 8 | SELECT d.deal_type 9 | , avg(e.engagement_count) AS "Average Engagements to Close" 10 | FROM hubspot_deals_consolidated d 11 | INNER JOIN ( 12 | SELECT deal_id 13 | , count(engagement_id) AS engagement_count 14 | FROM hubspot_engagements_consolidated 15 | GROUP BY 1 16 | ) e ON d.deal_id = e.deal_id 17 | WHERE d.deal_stage IN ('closedwon', 'closedlost') 18 | GROUP BY 1 19 | ORDER BY 2; 20 | ``` 21 | 22 | ## Query Results Dictionary 23 | 24 | | Column | Description | 25 | | --- | --- | 26 | | `deal_type`| The type of the deal. | 27 | | `Average Engagements to Close`| The average number of engagements to close the deal. | 28 | -------------------------------------------------------------------------------- /hubspot/queries/contact-to-mql.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: Elapsed Time from Contact to MQL (Histogram) 3 | description: This query quantifies how long it takes for Hubspot contacts to become Marketing Qualified Leads. Contacts are grouped into bins based on the elapsed time from contact to MQL. ('Day 1', 'Day 2-7', 'Day 8-28' and 'Days 28+'). This data is pulled from the [Hubspot API into Panoply](panoply.io/docs/data-sources/hubspot/). 4 | usage: This query can be used to create histogram of time elapsed between the creation of a contact and their conversion to a marketing qualified lead. You can follow the [full tutorial here](blog.panoply.io/hubspot-lead-analytics-in-sql-beyond-crm-reporting). 5 | modifications: Change the bin size by altering BETWEEN integers (Ex. BETWEEN 1 AND 6) and the associated column names (in single quotes after THEN). 6 | --- 7 | 8 | ```sql 9 | SELECT 10 | CASE WHEN DATEDIFF( 11 | 'day', "createdate", "hs_lifecyclestage_marketingqualifiedlead_date") = 0 12 | THEN 'Day 1' 13 | WHEN DATEDIFF( 14 | 'day', "createdate", "hs_lifecyclestage_marketingqualifiedlead_date") 15 | BETWEEN 1 AND 6 THEN 'Day 2-7' 16 | WHEN DATEDIFF( 17 | 'day', "createdate", "hs_lifecyclestage_marketingqualifiedlead_date") 18 | BETWEEN 6 AND 27 THEN 'Day 8-28' 19 | ELSE 'Days 28+' END AS bins_days_diff, 20 | COUNT(distinct hubspot_contacts_id) 21 | FROM 22 | ( 23 | SELECT 24 | p.hubspot_contacts_id, 25 | MIN( 26 | CASE WHEN "key" = 'createdate' THEN timestamp 'epoch' + p.value / 1000 27 | * interval '1 second' ELSE NULL END 28 | ) createdate, 29 | MIN( 30 | CASE WHEN "key" = 'hs_lifecyclestage_marketingqualifiedlead_date' THEN 31 | timestamp 'epoch' + p.value / 1000 * interval '1 second' ELSE NULL END 32 | ) hs_lifecyclestage_marketingqualifiedlead_date 33 | FROM 34 | hubspot_contacts_properties p 35 | GROUP BY 1 36 | ) contact_dates 37 | WHERE 38 | createdate IS NOT NULL 39 | AND hs_lifecyclestage_marketingqualifiedlead_date IS NOT NULL 40 | AND bins_days_diff IS NOT NULL 41 | GROUP by bins_days_diff 42 | ORDER BY bins_days_diff 43 | ``` 44 | 45 | ## Query Results Dictionary 46 | 47 | | Column | Description | 48 | | --- | --- | 49 | | `bins_days_diff`| Bin indicating the elapsed between contact creation and MQL conversion date. | 50 | | `count`| The number of Hubspot contacts that fall into the bins_days_diff groupings. | 51 | -------------------------------------------------------------------------------- /hubspot/queries/expected-revenue-by-close-month.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: Expected Revenue by Close Month 3 | description: This query calculates the total forecasted revenue per month. It does not include deals that were either closed won or closed lost. 4 | usage: This query can be used to create a line or bar chart of forecasted revenue per month. 5 | --- 6 | 7 | ```sql 8 | SELECT date_part(YEAR, close_date) AS "Close Year" 9 | , date_part(MONTH, close_date) AS "Close Month" 10 | , sum(forecast_amount) AS "Pipeline - Forecasted Amount" 11 | FROM hubspot_deals_consolidated 12 | WHERE deal_stage NOT IN ('closedwon', 'closedlost') 13 | GROUP BY 1, 2 14 | ORDER BY 1, 2; 15 | ``` 16 | 17 | ## Query Results Dictionary 18 | 19 | | Column | Description | 20 | | --- | --- | 21 | | `Close Year`| Year of closing date. | 22 | | `Close Month`| Month of closing date. | 23 | | `Pipeline - Forecasted Amount`| The total forecasted revenue. | 24 | -------------------------------------------------------------------------------- /hubspot/queries/expected-revenue-by-hubspot-owner.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: Expected Revenue by Hubspot Owner 3 | description: This query calculates the total forecasted revenue per Hubspot owner. It does not include deals that were either closed won or closed lost. 4 | usage: This query can be used to create histogram of sales representatives and their forecasted revenue. 5 | --- 6 | 7 | ```sql 8 | SELECT o.owner_id 9 | , o.first_name 10 | , o.last_name 11 | , sum(d.forecast_amount) AS "Pipeline - Forecasted Amount" 12 | FROM hubspot_deals_consolidated d 13 | INNER JOIN hubspot_owners_consolidated o ON d.owner_id = o.owner_id 14 | WHERE deal_stage NOT IN ('closedwon', 'closedlost') 15 | GROUP BY 1, 2, 3 16 | ORDER BY 4 DESC; 17 | ``` 18 | 19 | ## Query Results Dictionary 20 | 21 | | Column | Description | 22 | | --- | --- | 23 | | `owner_id`| The identifier of the Hubspot owner. | 24 | | `first_name`| The first name of the Hubspot owner. | 25 | | `last_name`| The last name of the Hubspot owner. | 26 | | `Pipeline - Forecasted Amount`| The total forecasted revenue. | 27 | -------------------------------------------------------------------------------- /hubspot/queries/form-submissions-analysis.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: Form Submission Analysis 3 | description: This query calculates metrics about form submissions and the context of the form submissions. This data is pulled from the [Hubspot API into Panoply](https://panoply.io/docs/data-sources/hubspot/). 4 | usage: This query can be used to create a bar chart that identifies the most commonly submitted Hubspot forms. Further modification could allow you to analyze the form submission over time, or the sequence of form submissions for each contact. 5 | modifications: Remove the dimensions of the query to aggregate the metrics at a higher level. For instance, if one form can be submitted on multiple pages, removing the `page_url` and `page_title` dimensions would provide _form-level_ metrics only. Modify the `submission_timestamp` comparison in the `WHERE` clause to change the time frame of the analysis. 6 | --- 7 | 8 | ```sql 9 | WITH form_submissions 10 | AS ( 11 | SELECT s."conversion-id" submission_id, 12 | TIMESTAMP 'epoch' + s.TIMESTAMP / 1000 * INTERVAL '1 second' submission_timestamp, 13 | row_number() OVER ( 14 | PARTITION BY s.hubspot_contacts_id ORDER BY s.TIMESTAMP 15 | ) submission_order, 16 | s."page-id" page_id, 17 | s."page-url" page_url, 18 | s."page-title" page_title, 19 | s.title form_title, 20 | s."form-id" form_id, 21 | s.hubspot_contacts_id 22 | FROM "hubspot_contacts_form-submissions" s 23 | ) 24 | SELECT f.guid form_id, 25 | f.name form_current_name, 26 | fs.page_url, 27 | fs.page_title, 28 | fs.form_title, 29 | count(DISTINCT fs.submission_id) total_submissions, 30 | count(DISTINCT fs.hubspot_contacts_id) total_contacts, 31 | count(DISTINCT CASE WHEN submission_order = 1 THEN fs.submission_id END) first_submissions, 32 | round(total_submissions::FLOAT / total_contacts, 2) submission_per_contact 33 | FROM hubspot_forms f 34 | JOIN form_submissions fs ON fs.form_id = f.guid 35 | WHERE submission_timestamp > CURRENT_DATE - 28 36 | GROUP BY 1,2,3,4,5; 37 | ``` 38 | 39 | ## Query Results Dictionary 40 | 41 | | Column | Description | 42 | | --- | --- | 43 | | `form_id`| Hubspot's internal canonical form identifier, sometimes referred to as the `guid` | 44 | | `form_current_name`| Most recent name of the form in the Hubspot app (form names are mutable) | 45 | | `page_url`| The full URL the form was submitted on excluding protocol | 46 | | `page_title`| The title of the page where the form was submitted at the time of submission | 47 | | `form_title`| The name of the form at the time of submission | 48 | | `total_submissions`| Count of unique form submissions for each form, also referred to as "conversions" | 49 | | `total_contacts`| Count of unique contacts who submitted each form | 50 | | `first_submissions`| Count of times that this was a contacts' first form submission (often point of | acquisition) 51 | | `submission_per_contact`| Average number of submission per contact | 52 | -------------------------------------------------------------------------------- /hubspot/queries/lifetime-value-and-lifetime-deal-count.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: Lifetime Value and Lifetime Deal Count 3 | description: This query calculates the lifetime value and number of deals per company 4 | usage: This query can be used to create a list of all companies and their LTV and LTD. 5 | modifications: Additional filters can be added, both on companies properties and/or the calculated LTV and LTD 6 | --- 7 | 8 | ```sql 9 | SELECT cp.company_name 10 | , coalesce(SUM(d.closed_amount_in_home_currency), 0) AS LTV 11 | , coalesce(COUNT(d.deal_id), 0) AS LTD 12 | FROM hubspot_companies_consolidated cp 13 | LEFT JOIN hubspot_deals_companies dc ON dc.company_id = cp.company_id 14 | INNER JOIN hubspot_deals_consolidated d ON d.deal_id = dc.deal_id 15 | WHERE d.deal_stage = 'closedwon' 16 | GROUP BY 1 17 | ORDER BY 2 DESC, 3 DESC; 18 | ``` 19 | 20 | ## Query Results Dictionary 21 | 22 | | Column | Description | 23 | | --- | --- | 24 | | `company_name`| Company name. | 25 | | `LTV`| Lifetime value. | 26 | | `LTD`| Lifetime deals count. | 27 | -------------------------------------------------------------------------------- /hubspot/queries/market-cap-companies.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: Company Count by Market Cap 3 | description: This query counts how many companies are in each cap size ('Small Cap', 'Mid Cap' and 'Large Cap'). This data is pulled from the [Hubspot API into Panoply](panoply.io/docs/data-sources/hubspot/). 4 | usage: This query can be used to create histogram of companies in each cap size. 5 | modifications: Change the bin size by altering the integers in the CASE statement and split the groupings to additional bins by adding additional conditions. 6 | --- 7 | 8 | ```sql 9 | SELECT CASE WHEN annual_revenue < 1000000000 THEN 'Small Cap' WHEN annual_revenue < 10000000000 THEN 'Mid Cap' ELSE 'Large Cap' END AS "Cap Size" 10 | , count(company_id) AS "Count Companies" 11 | FROM hubspot_companies_consolidated 12 | GROUP BY 1; 13 | ``` 14 | 15 | ## Query Results Dictionary 16 | 17 | | Column | Description | 18 | | --- | --- | 19 | | `Cap Size`| Bin indicating the annual revenue. | 20 | | `Count Companies`| The number of Hubspot companies that fall into the cap size groupings. | 21 | -------------------------------------------------------------------------------- /hubspot/queries/number-of-contacts-by-city.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: Number of Contacts by City 3 | description: This query counts how many contacts are from each city. 4 | usage: This query can be used to create histogram of contacts in from each city. 5 | modifications: Add filters on the number of contacts in each city using HAVING statement. 6 | --- 7 | 8 | ```sql 9 | SELECT CASE WHEN city IS NULL THEN 'Unspecified' ELSE city END AS "City" 10 | , count(contact_id) AS count_contacts 11 | FROM hubspot_contacts_consolidated 12 | GROUP BY 1 13 | ORDER BY 2 DESC; 14 | ``` 15 | 16 | ## Query Results Dictionary 17 | 18 | | Column | Description | 19 | | --- | --- | 20 | | `City`| The contact's city. | 21 | | `count_contacts`| The number of Hubspot contacts that fall into the city groupings. | 22 | -------------------------------------------------------------------------------- /hubspot/queries/number-of-contacts-by-source.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: Number of contacts by Source 3 | description: This query counts how many contacts came from each source. 4 | usage: This query can be used to create histogram of contacts in from each source. 5 | modifications: Add filters on the number of contacts in each source using HAVING statement. 6 | --- 7 | 8 | ```sql 9 | SELECT source 10 | , count(contact_id) count_contacts 11 | FROM hubspot_contacts_consolidated 12 | GROUP BY 1 13 | ORDER BY 2 DESC; 14 | ``` 15 | 16 | ## Query Results Dictionary 17 | 18 | | Column | Description | 19 | | --- | --- | 20 | | `source`| The source from which the contacts came. | 21 | | `count_contacts`| The number of Hubspot contacts that fall into the source groupings. | 22 | -------------------------------------------------------------------------------- /hubspot/views/companies.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: Companies Report 3 | description: Denormalized view of Hubspot's default values in `hubspot_companies_properties` key-value pair table 4 | usage: This is the "ready for analysis" view of Hubspot companies 5 | modifications: Properties can be removed or added. To add custom properties, refer to your Hubspot properties setting for their name. Dates are formatted as Unix timestamps so they must be transformed and cast into dates. 6 | --- 7 | 8 | ```sql 9 | -- CREATE VIEW hubspot_companies_report AS 10 | SELECT 11 | -- COMPANY DETAILS -- 12 | p.hubspot_company_id, 13 | max( CASE WHEN key = 'about_us' THEN value END ) AS about_us, -- About Us (string) 14 | max( CASE WHEN key = 'hs_all_accessible_team_ids' THEN value END ) AS hs_all_accessible_team_ids, -- All accessible team ids (enumeration) 15 | max( CASE WHEN key = 'hs_all_owner_ids' THEN value END ) AS hs_all_owner_ids, -- All owner ids (enumeration) 16 | max( CASE WHEN key = 'hs_all_team_ids' THEN value END ) AS hs_all_team_ids, -- All team ids (enumeration) 17 | max( CASE WHEN key = 'annualrevenue' THEN value END ) AS annualrevenue, -- Annual Revenue (number) 18 | max( CASE WHEN key = 'num_associated_contacts' THEN value END ) AS num_associated_contacts, -- Associated Contacts (number) 19 | max( CASE WHEN key = 'num_associated_deals' THEN value END ) AS num_associated_deals, -- Associated Deals (number) 20 | max( CASE WHEN key = 'city' THEN value END ) AS city, -- City (string) 21 | max( CASE WHEN key = 'closedate' THEN timestamp 'epoch' + value / 1000 * interval '1 second' END ) AS closedate, -- Close Date (timestamp) 22 | max( CASE WHEN key = 'domain' THEN value END ) AS domain, -- Company Domain Name (string) 23 | max( CASE WHEN key = 'hs_object_id' THEN value END ) AS hs_object_id, -- Company ID (number) 24 | max( CASE WHEN key = 'hubspot_owner_id' THEN value END ) AS hubspot_owner_id, -- Company owner (enumeration) 25 | max( CASE WHEN key = 'country' THEN value END ) AS country, -- Country (string) 26 | max( CASE WHEN key = 'createdate' THEN timestamp 'epoch' + value / 1000 * interval '1 second' END ) AS createdate, -- Create Date (timestamp) 27 | max( CASE WHEN key = 'days_to_close' THEN value END ) AS days_to_close, -- Days to Close (number) 28 | max( CASE WHEN key = 'description' THEN value END ) AS description, -- Description (string) 29 | max( CASE WHEN key = 'facebook_company_page' THEN value END ) AS facebook_company_page, -- Facebook Company Page (string) 30 | max( CASE WHEN key = 'first_contact_createdate' THEN timestamp 'epoch' + value / 1000 * interval '1 second' END ) AS first_contact_createdate, -- First Contact Create Date (timestamp) 31 | max( CASE WHEN key = 'first_conversion_event_name' THEN value END ) AS first_conversion_event_name, -- First Conversion (string) 32 | max( CASE WHEN key = 'first_conversion_date' THEN timestamp 'epoch' + value / 1000 * interval '1 second' END ) AS first_conversion_date, -- First Conversion Date (timestamp) 33 | max( CASE WHEN key = 'first_deal_created_date' THEN timestamp 'epoch' + value / 1000 * interval '1 second' END ) AS first_deal_created_date, -- First Deal Created Date (timestamp) 34 | max( CASE WHEN key = 'hs_analytics_first_touch_converting_campaign' THEN value END ) AS hs_analytics_first_touch_converting_campaign, -- First Touch Converting Campaign (string) 35 | max( CASE WHEN key = 'hubspot_team_id' THEN value END ) AS hubspot_team_id, -- HubSpot Team (enumeration) 36 | max( CASE WHEN key = 'industry' THEN value END ) AS industry, -- Industry (enumeration) 37 | max( CASE WHEN key = 'is_public' THEN value END ) AS is_public, -- Is Public (bool) 38 | max( CASE WHEN key = 'notes_last_updated' THEN timestamp 'epoch' + value / 1000 * interval '1 second' END ) AS notes_last_updated, -- Last Activity Date (timestamp) 39 | max( CASE WHEN key = 'notes_last_contacted' THEN timestamp 'epoch' + value / 1000 * interval '1 second' END ) AS notes_last_contacted, -- Last Contacted (timestamp) 40 | max( CASE WHEN key = 'engagements_last_meeting_booked' THEN timestamp 'epoch' + value / 1000 * interval '1 second' END ) AS engagements_last_meeting_booked, -- Last Meeting Booked (timestamp) 41 | max( CASE WHEN key = 'hs_lastmodifieddate' THEN timestamp 'epoch' + value / 1000 * interval '1 second' END ) AS hs_lastmodifieddate, -- Last Modified Date (timestamp) 42 | max( CASE WHEN key = 'hs_analytics_last_touch_converting_campaign' THEN value END ) AS hs_analytics_last_touch_converting_campaign, -- Last Touch Converting Campaign (string) 43 | max( CASE WHEN key = 'hs_lead_status' THEN value END ) AS hs_lead_status, -- Lead Status (enumeration) 44 | max( CASE WHEN key = 'lifecyclestage' THEN value END ) AS lifecyclestage, -- Lifecycle Stage (enumeration) 45 | max( CASE WHEN key = 'hs_predictivecontactscore_v2' THEN value END ) AS hs_predictivecontactscore_v2, -- Likelihood to close (number) 46 | max( CASE WHEN key = 'linkedinbio' THEN value END ) AS linkedinbio, -- LinkedIn Bio (string) 47 | max( CASE WHEN key = 'linkedin_company_page' THEN value END ) AS linkedin_company_page, -- LinkedIn Company Page (string) 48 | max( CASE WHEN key = 'name' THEN value END ) AS name, -- Name (string) 49 | max( CASE WHEN key = 'notes_next_activity_date' THEN timestamp 'epoch' + value / 1000 * interval '1 second' END ) AS notes_next_activity_date, -- Next Activity Date (timestamp) 50 | max( CASE WHEN key = 'numberofemployees' THEN value END ) AS numberofemployees, -- Number of Employees (number) 51 | max( CASE WHEN key = 'num_conversion_events' THEN value END ) AS num_conversion_events, -- Number of Form Submissions (number) 52 | max( CASE WHEN key = 'hs_analytics_num_page_views' THEN value END ) AS hs_analytics_num_page_views, -- Number of Pageviews (number) 53 | max( CASE WHEN key = 'num_notes' THEN value END ) AS num_notes, -- Number of Sales Activities (number) 54 | max( CASE WHEN key = 'hs_analytics_num_visits' THEN value END ) AS hs_analytics_num_visits, -- Number of Sessions (number) 55 | max( CASE WHEN key = 'num_contacted_notes' THEN value END ) AS num_contacted_notes, -- Number of times contacted (number) 56 | max( CASE WHEN key = 'hs_analytics_source_data_1' THEN value END ) AS hs_analytics_source_data_1, -- Original Source Data 1 (string) 57 | max( CASE WHEN key = 'hs_analytics_source_data_2' THEN value END ) AS hs_analytics_source_data_2, -- Original Source Data 2 (string) 58 | max( CASE WHEN key = 'hs_analytics_source' THEN value END ) AS hs_analytics_source, -- Original Source Type (enumeration) 59 | max( CASE WHEN key = 'hubspot_owner_assigneddate' THEN timestamp 'epoch' + value / 1000 * interval '1 second' END ) AS hubspot_owner_assigneddate, -- Owner Assigned Date (timestamp) 60 | max( CASE WHEN key = 'phone' THEN value END ) AS phone, -- Phone Number (string) 61 | max( CASE WHEN key = 'zip' THEN value END ) AS zip, -- Postal Code (string) 62 | max( CASE WHEN key = 'recent_conversion_event_name' THEN value END ) AS recent_conversion_event_name, -- Recent Conversion (string) 63 | max( CASE WHEN key = 'recent_conversion_date' THEN timestamp 'epoch' + value / 1000 * interval '1 second' END ) AS recent_conversion_date, -- Recent Conversion Date (timestamp) 64 | max( CASE WHEN key = 'recent_deal_amount' THEN value END ) AS recent_deal_amount, -- Recent Deal Amount (number) 65 | max( CASE WHEN key = 'recent_deal_close_date' THEN timestamp 'epoch' + value / 1000 * interval '1 second' END ) AS recent_deal_close_date, -- Recent Deal Close Date (timestamp) 66 | max( CASE WHEN key = 'hs_sales_email_last_replied' THEN timestamp 'epoch' + value / 1000 * interval '1 second' END ) AS hs_sales_email_last_replied, -- Recent Sales Email Replied Date (timestamp) 67 | max( CASE WHEN key = 'state' THEN value END ) AS state, -- State/Region (string) 68 | max( CASE WHEN key = 'address' THEN value END ) AS address, -- Street Address (string) 69 | max( CASE WHEN key = 'address2' THEN value END ) AS address2, -- Street Address 2 (string) 70 | max( CASE WHEN key = 'hs_analytics_first_timestamp' THEN timestamp 'epoch' + value / 1000 * interval '1 second' END ) AS hs_analytics_first_timestamp, -- Time First Seen (timestamp) 71 | max( CASE WHEN key = 'hs_analytics_last_timestamp' THEN timestamp 'epoch' + value / 1000 * interval '1 second' END ) AS hs_analytics_last_timestamp, -- Time Last Seen (timestamp) 72 | max( CASE WHEN key = 'timezone' THEN value END ) AS timezone, -- Time Zone (string) 73 | max( CASE WHEN key = 'hs_analytics_first_visit_timestamp' THEN timestamp 'epoch' + value / 1000 * interval '1 second' END ) AS hs_analytics_first_visit_timestamp, -- Time of First Session (timestamp) 74 | max( CASE WHEN key = 'hs_analytics_last_visit_timestamp' THEN timestamp 'epoch' + value / 1000 * interval '1 second' END ) AS hs_analytics_last_visit_timestamp, -- Time of Last Session (timestamp) 75 | max( CASE WHEN key = 'total_money_raised' THEN value END ) AS total_money_raised, -- Total Money Raised (string) 76 | max( CASE WHEN key = 'total_revenue' THEN value END ) AS total_revenue, -- Total Revenue (number) 77 | max( CASE WHEN key = 'twitterhandle' THEN value END ) AS twitterhandle, -- Twitter Handle (string) 78 | max( CASE WHEN key = 'type' THEN value END ) AS type, -- Type (enumeration) 79 | max( CASE WHEN key = 'web_technologies' THEN value END ) AS web_technologies, -- Web Technologies (enumeration) 80 | max( CASE WHEN key = 'website' THEN value END ) AS website, -- Website URL (string) 81 | max( CASE WHEN key = 'founded_year' THEN value END ) AS founded_year -- Year Founded (string) 82 | FROM hubspot_company_properties p 83 | GROUP BY p.hubspot_company_id; 84 | ``` 85 | 86 | ## Example: Company creation by month 87 | 88 | ```sql 89 | SELECT 90 | date_trunc('month', createdate) create_month, 91 | count(distinct hubspot_companies_id) company_count 92 | FROM hubspot_companies_report 93 | GROUP BY create_month 94 | ORDER BY create_month; 95 | ``` 96 | -------------------------------------------------------------------------------- /hubspot/views/companies_consolidated.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: Companies consolidated 3 | description: Denormalized view of Hubspot's companies data. This view can be used together with other Hubspot views 4 | usage: This is the "ready for analysis" view of Hubspot companies 5 | modifications: Properties can be removed or added. To add custom properties, refer to your Hubspot properties setting for their name. Dates are formatted as Unix timestamps so they must be transformed and cast into dates. 6 | --- 7 | 8 | ```sql 9 | -- CREATE VIEW hubspot_companies_consolidated AS 10 | SELECT cp.* 11 | FROM ( 12 | SELECT companyid as company_id 13 | FROM hubspot_company 14 | WHERE isdeleted IS FALSE 15 | ) hc 16 | INNER JOIN ( 17 | SELECT p.hubspot_company_id as company_id 18 | -- Company Details 19 | , MAX(CASE WHEN KEY = 'name' THEN value END) AS company_name -- Name (string) 20 | , MAX(CASE WHEN KEY = 'about_us' THEN value END) AS about_us -- About Us (string) 21 | , MAX(CASE WHEN KEY = 'description' THEN value END) AS description -- Description (string) 22 | , MAX(CASE WHEN KEY = 'domain' THEN value END) AS domain -- Company Domain Name (string) 23 | , MAX(CASE WHEN KEY = 'phone' THEN value END) AS phone -- Phone Number (string) 24 | , MAX(CASE WHEN KEY = 'address' THEN value END) AS address -- Street Address (string) 25 | , MAX(CASE WHEN KEY = 'address2' THEN value END) AS address2 -- Street Address 2 (string) 26 | , MAX(CASE WHEN KEY = 'city' THEN value END) AS city -- City (string) 27 | , MAX(CASE WHEN KEY = 'state' THEN value END) AS "state" -- State/Region (string) 28 | , MAX(CASE WHEN KEY = 'zip' THEN value END) AS zip -- Postal Code (string) 29 | , MAX(CASE WHEN KEY = 'country' THEN value END) AS country -- Country (string) 30 | , MAX(CASE WHEN KEY = 'timezone' THEN value END) AS timezone -- Time Zone (string) 31 | , MAX(CASE WHEN KEY = 'industry' THEN value END) AS industry -- Industry (enumeration) 32 | , MAX(CASE WHEN KEY = 'is_public' THEN value END) AS is_public -- Is Public (bool) 33 | , MAX(CASE WHEN KEY = 'linkedinbio' THEN value END) AS linkedin_bio -- LinkedIn Bio (string) 34 | , MAX(CASE WHEN KEY = 'linkedin_company_page' THEN value END) AS linkedin_company_page -- LinkedIn Company Page (string) 35 | , MAX(CASE WHEN KEY = 'facebook_company_page' THEN value END) AS facebook_company_page -- Facebook Company Page (string) 36 | , MAX(CASE WHEN KEY = 'twitterhandle' THEN value END) AS twitter_handle -- Twitter Handle (string) 37 | , MAX(CASE WHEN KEY = 'web_technologies' THEN value END) AS web_technologies -- Web Technologies (enumeration) 38 | , MAX(CASE WHEN KEY = 'website' THEN value END) AS website -- Website URL (string) 39 | , MAX(CASE WHEN KEY = 'founded_year' THEN value END) AS founded_year -- Year Founded (string) 40 | , MAX(CASE WHEN KEY = 'numberofemployees' THEN value END) AS number_of_employees -- Number of Employees (number) 41 | -- Dates and Durations 42 | , MAX(CASE WHEN KEY = 'createdate' THEN TIMESTAMP 'epoch' + value / 1000 * interval '1 second' END) AS created_date -- Create Date (timestamp) 43 | , MAX(CASE WHEN KEY = 'closedate' THEN TIMESTAMP 'epoch' + value / 1000 * interval '1 second' END) AS close_date -- Close Date (timestamp) 44 | , MAX(CASE WHEN KEY = 'days_to_close' THEN value END) AS days_to_close -- Days to Close (number) 45 | , MAX(CASE WHEN KEY = 'first_contact_createdate' THEN TIMESTAMP 'epoch' + value / 1000 * interval '1 second' END) AS first_contact_date -- First Contact Create Date (timestamp) 46 | , MAX(CASE WHEN KEY = 'first_conversion_date' THEN TIMESTAMP 'epoch' + value / 1000 * interval '1 second' END) AS first_conversion_date -- First Conversion Date (timestamp) 47 | , MAX(CASE WHEN KEY = 'first_deal_created_date' THEN TIMESTAMP 'epoch' + value / 1000 * interval '1 second' END) AS first_deal_date -- First Deal Created Date (timestamp) 48 | , MAX(CASE WHEN KEY = 'notes_last_updated' THEN TIMESTAMP 'epoch' + value / 1000 * interval '1 second' END) AS notes_last_updated -- Last Activity Date (timestamp) 49 | , MAX(CASE WHEN KEY = 'notes_last_contacted' THEN TIMESTAMP 'epoch' + value / 1000 * interval '1 second' END) AS notes_last_contacted -- Last Contacted (timestamp) 50 | , MAX(CASE WHEN KEY = 'engagements_last_meeting_booked' THEN TIMESTAMP 'epoch' + value / 1000 * interval '1 second' END) AS engagements_last_meeting_booked -- Last Meeting Booked (timestamp) 51 | , MAX(CASE WHEN KEY = 'hs_lastmodifieddate' THEN TIMESTAMP 'epoch' + value / 1000 * interval '1 second' END) AS last_modified_date -- Last Modified Date (timestamp) 52 | , MAX(CASE WHEN KEY = 'notes_next_activity_date' THEN TIMESTAMP 'epoch' + value / 1000 * interval '1 second' END) AS notes_next_activity_date -- Next Activity Date (timestamp) 53 | , MAX(CASE WHEN KEY = 'hubspot_owner_assigneddate' THEN TIMESTAMP 'epoch' + value / 1000 * interval '1 second' END) AS owner_assigned_date -- Owner Assigned Date (timestamp) 54 | , MAX(CASE WHEN KEY = 'recent_conversion_date' THEN TIMESTAMP 'epoch' + value / 1000 * interval '1 second' END) AS recent_conversion_date -- Recent Conversion Date (timestamp) 55 | , MAX(CASE WHEN KEY = 'recent_deal_close_date' THEN TIMESTAMP 'epoch' + value / 1000 * interval '1 second' END) AS recent_deal_close_date -- Recent Deal Close Date (timestamp) 56 | , MAX(CASE WHEN KEY = 'hs_sales_email_last_replied' THEN TIMESTAMP 'epoch' + value / 1000 * interval '1 second' END) AS sales_email_last_replied -- Recent Sales Email Replied Date (timestamp) 57 | , MAX(CASE WHEN KEY = 'hs_analytics_first_timestamp' THEN TIMESTAMP 'epoch' + value / 1000 * interval '1 second' END) AS first_timestamp -- Time First Seen (timestamp) 58 | , MAX(CASE WHEN KEY = 'hs_analytics_last_timestamp' THEN TIMESTAMP 'epoch' + value / 1000 * interval '1 second' END) AS last_timestamp -- Time Last Seen (timestamp) 59 | , MAX(CASE WHEN KEY = 'hs_analytics_first_visit_timestamp' THEN TIMESTAMP 'epoch' + value / 1000 * interval '1 second' END) AS first_visit_timestamp -- Time of First Session (timestamp) 60 | , MAX(CASE WHEN KEY = 'hs_analytics_last_visit_timestamp' THEN TIMESTAMP 'epoch' + value / 1000 * interval '1 second' END) AS last_visit_timestamp -- Time of Last Session (timestamp) 61 | -- Money 62 | , MAX(CASE WHEN KEY = 'annualrevenue' THEN value END) AS annual_revenue -- Annual Revenue (number) 63 | , MAX(CASE WHEN KEY = 'recent_deal_amount' THEN value END) AS recent_deal_amount -- Recent Deal Amount (number) 64 | , MAX(CASE WHEN KEY = 'total_money_raised' THEN value END) AS total_money_raised -- Total Money Raised (string) 65 | , MAX(CASE WHEN KEY = 'total_revenue' THEN value END) AS total_revenue -- Total Revenue (number) 66 | -- Data Source Fields 67 | , MAX(CASE WHEN KEY = 'hs_analytics_source' THEN value END) AS source -- Original Source Type (enumeration) 68 | , MAX(CASE WHEN KEY = 'hs_analytics_source_data_1' THEN value END) AS source_data_1 -- Original Source Data 1 (string) 69 | , MAX(CASE WHEN KEY = 'hs_analytics_source_data_2' THEN value END) AS source_data_2 -- Original Source Data 2 (string) 70 | -- Sales Pipeline Info 71 | , MAX(CASE WHEN KEY = 'hubspot_owner_id' THEN CAST(value as INT) END) AS owner_id -- Company owner (enumeration) 72 | , MAX(CASE WHEN KEY = 'hs_all_owner_ids' THEN value END) AS all_owner_ids -- All owner ids (enumeration) 73 | , MAX(CASE WHEN KEY = 'hs_lead_status' THEN value END) AS lead_status -- Lead Status (enumeration) 74 | , MAX(CASE WHEN KEY = 'lifecyclestage' THEN value END) AS lifecycle_stage -- Lifecycle Stage (enumeration) 75 | , MAX(CASE WHEN KEY = 'first_conversion_event_name' THEN value END) AS first_conversion_event -- First Conversion (string) 76 | , MAX(CASE WHEN KEY = 'recent_conversion_event_name' THEN value END) AS recent_conversion_event -- Recent Conversion (string) 77 | -- Numbers 78 | , MAX(CASE WHEN KEY = 'num_associated_contacts' THEN value END) AS num_associated_contacts -- Associated Contacts (number) 79 | , MAX(CASE WHEN KEY = 'num_associated_deals' THEN value END) AS num_associated_deals -- Associated Deals (number) 80 | , MAX(CASE WHEN KEY = 'num_conversion_events' THEN value END) AS num_conversion_events -- Number of Form Submissions (number) 81 | , MAX(CASE WHEN KEY = 'hs_analytics_num_page_views' THEN value END) AS num_page_views -- Number of Pageviews (number) 82 | , MAX(CASE WHEN KEY = 'num_notes' THEN value END) AS num_notes -- Number of Sales Activities (number) 83 | , MAX(CASE WHEN KEY = 'hs_analytics_num_visits' THEN value END) AS num_visits -- Number of Sessions (number) 84 | , MAX(CASE WHEN KEY = 'num_contacted_notes' THEN value END) AS num_contacted_notes -- Number of times contacted (number) 85 | FROM hubspot_company_properties p 86 | GROUP BY p.hubspot_company_id 87 | ) cp ON cp.company_id = hc.company_id; 88 | ``` 89 | -------------------------------------------------------------------------------- /hubspot/views/contacts.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: Contacts Report 3 | description: Denormalized view of Hubspot's default values in `hubspot_contacts_properties` key-value pair table 4 | usage: This is the "ready for analysis" view of Hubspot contacts 5 | modifications: Properties can be removed or added. To add custom properties, refer to your Hubspot properties setting for their name. Dates are formatted as Unix timestamps so they must be transformed and cast into dates. 6 | --- 7 | 8 | ```sql 9 | -- CREATE VIEW hubspot_contacts_report AS 10 | SELECT 11 | -- CONTACT DETAILS -- 12 | p.hubspot_contacts_id, 13 | max( CASE WHEN key = 'hs_object_id' THEN value END ) AS hs_object_id, -- Contact ID (number) 14 | max( CASE WHEN key = 'email' THEN value END ) AS email, -- Email (string) 15 | max( CASE WHEN key = 'hs_email_domain' THEN value END ) AS hs_email_domain, -- Email Domain (string) 16 | max( CASE WHEN key = 'firstname' THEN value END ) AS firstname, -- First Name (string) 17 | max( CASE WHEN key = 'lastname' THEN value END ) AS lastname, -- Last Name (string) 18 | max( CASE WHEN key = 'twitterhandle' THEN value END ) AS twitterhandle, -- Twitter Username (string) 19 | max( CASE WHEN key = 'createdate' THEN timestamp 'epoch' + value / 1000 * interval '1 second' END ) AS createdate, -- Create Date (timestamp) 20 | max( CASE WHEN key = 'lastmodifieddate' THEN timestamp 'epoch' + value / 1000 * interval '1 second' END ) AS lastmodifieddate, -- Last Modified Date (timestamp) 21 | max( CASE WHEN key = 'hs_lifecyclestage_lead_date' THEN timestamp 'epoch' + value / 1000 * interval '1 second' END ) AS hs_lifecyclestage_lead_date, -- Became a Lead Date (timestamp) 22 | max( CASE WHEN key = 'city' THEN value END ) AS city, -- City (string) 23 | max( CASE WHEN key = 'state' THEN value END ) AS state, -- State/Region (string) 24 | max( CASE WHEN key = 'jobtitle' THEN value END ) AS jobtitle, -- Job Title (string) 25 | max( CASE WHEN key = 'lifecyclestage' THEN value END ) AS lifecyclestage, -- Lifecycle Stage (enumeration) 26 | max( CASE WHEN key = 'company' THEN value END ) AS company, -- Company Name (string) 27 | max( CASE WHEN key = 'website' THEN value END ) AS website, -- Website URL (string) 28 | max( CASE WHEN key = 'hs_is_contact' THEN value END ) AS hs_is_contact, -- Is a contact (bool) 29 | max( CASE WHEN key = 'hs_all_contact_vids' THEN value END ) AS hs_all_contact_vids, -- All vids for a contact (enumeration) 30 | max( CASE WHEN key = 'hs_predictivecontactscore_v2' THEN value END ) AS hs_predictivecontactscore_v2, -- Likelihood to close (number) 31 | -- EMAIL DETAILS -- 32 | max( CASE WHEN key = 'hs_email_quarantined' THEN value END ) AS hs_email_quarantined, -- Email Address Quarantined (bool) 33 | max( CASE WHEN key = 'hs_email_recipient_fatigue_recovery_time' THEN timestamp 'epoch' + value / 1000 * interval '1 second' END ) AS hs_email_recipient_fatigue_recovery_time, -- Email Address Recipient Fatigue Next Available Sending Time (timestamp) 34 | max( CASE WHEN key = 'hs_email_optout_7352524' THEN value END ) AS hs_email_optout_7352524, -- Opted out of email: Customer Service Communication (enumeration) 35 | max( CASE WHEN key = 'hs_email_optout' THEN value END ) AS hs_email_optout, -- Unsubscribed from all email (bool) 36 | -- CONVERSION DETAILS -- 37 | max( CASE WHEN key = 'num_conversion_events' THEN value END ) AS num_conversion_events, -- Number of Form Submissions (number) 38 | max( CASE WHEN key = 'num_unique_conversion_events' THEN value END ) AS num_unique_conversion_events, -- Number of Unique Forms Submitted (number) 39 | -- ANALYTICS DETAILS -- 40 | max( CASE WHEN key = 'hs_analytics_first_touch_converting_campaign' THEN value END ) AS hs_analytics_first_touch_converting_campaign, -- First Touch Converting Campaign (string) 41 | max( CASE WHEN key = 'hs_analytics_last_touch_converting_campaign' THEN value END ) AS hs_analytics_last_touch_converting_campaign, -- Last Touch Converting Campaign (string) 42 | max( CASE WHEN key = 'hs_analytics_num_page_views' THEN value END ) AS hs_analytics_num_page_views, -- Number of Pageviews (number) 43 | max( CASE WHEN key = 'hs_analytics_num_visits' THEN value END ) AS hs_analytics_num_visits, -- Number of Sessions (number) 44 | max( CASE WHEN key = 'hs_analytics_num_event_completions' THEN value END ) AS hs_analytics_num_event_completions, -- Number of event completions (number) 45 | max( CASE WHEN key = 'hs_analytics_first_timestamp' THEN timestamp 'epoch' + value / 1000 * interval '1 second' END ) AS hs_analytics_first_timestamp, -- Time First Seen (timestamp) 46 | max( CASE WHEN key = 'hs_analytics_first_visit_timestamp' THEN timestamp 'epoch' + value / 1000 * interval '1 second' END ) AS hs_analytics_first_visit_timestamp, -- Time of First Session (timestamp) 47 | max( CASE WHEN key = 'hs_analytics_last_timestamp' THEN timestamp 'epoch' + value / 1000 * interval '1 second' END ) AS hs_analytics_last_timestamp, -- Time Last Seen (timestamp) 48 | max( CASE WHEN key = 'hs_analytics_last_visit_timestamp' THEN timestamp 'epoch' + value / 1000 * interval '1 second' END ) AS hs_analytics_last_visit_timestamp, -- Time of Last Session (timestamp) 49 | max( CASE WHEN key = 'hs_analytics_source' THEN value END ) AS hs_analytics_source, -- Original Source (enumeration) 50 | max( CASE WHEN key = 'hs_analytics_source_data_1' THEN value END ) AS hs_analytics_source_data_1, -- Original Source Drill-Down 1 (string) 51 | max( CASE WHEN key = 'hs_analytics_source_data_2' THEN value END ) AS hs_analytics_source_data_2, -- Original Source Drill-Down 2 (string) 52 | max( CASE WHEN key = 'hs_analytics_average_page_views' THEN value END ) AS hs_analytics_average_page_views, -- Average Pageviews (number) 53 | max( CASE WHEN key = 'hs_analytics_revenue' THEN value END ) AS hs_analytics_revenue -- Event Revenue (number) 54 | FROM hubspot_contacts_properties p 55 | GROUP BY p.hubspot_contacts_id; 56 | ``` 57 | 58 | ## Example: Contact creation by month 59 | 60 | ```sql 61 | SELECT 62 | date_trunc('month', createdate) create_month, 63 | count(distinct hubspot_contacts_id) contact_count 64 | FROM hubspot_contacts_report 65 | GROUP BY create_month 66 | ORDER BY create_month; 67 | ``` 68 | -------------------------------------------------------------------------------- /hubspot/views/contacts_consolidated.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: Contacts consolidated 3 | description: Denormalized view of Hubspot's contacts data. This view can be used together with other Hubspot views 4 | usage: This is the "ready for analysis" view of Hubspot contacts 5 | modifications: Properties can be removed or added. To add custom properties, refer to your Hubspot properties setting for their name. Dates are formatted as Unix timestamps so they must be transformed and cast into dates. 6 | --- 7 | 8 | ```sql 9 | -- CREATE VIEW hubspot_contacts_consolidated AS 10 | SELECT cp.* 11 | , hc.profile_url 12 | , hcm.merged_contact_id 13 | , hcm.merged_contact_first_name 14 | , hcm.merged_contact_last_name 15 | , hcm.merged_contact_properties_moved 16 | FROM ( 17 | SELECT id as contact_id 18 | , "profile-url" AS profile_url 19 | FROM hubspot_contacts 20 | WHERE case when "is-contact" = 1 then TRUE when "is-contact" = 0 then FALSE else "is-contact"::bigint::boolean end IS TRUE 21 | ) hc 22 | INNER JOIN ( 23 | SELECT p.hubspot_contacts_id as contact_id 24 | -- Contact Info 25 | , MAX(CASE WHEN KEY = 'email' THEN value END) AS email -- Email (string) 26 | , MAX(CASE WHEN KEY = 'hs_email_domain' THEN value END) AS email_domain -- Email Domain (string) 27 | , MAX(CASE WHEN KEY = 'firstname' THEN value END) AS first_name -- First Name (string) 28 | , MAX(CASE WHEN KEY = 'lastname' THEN value END) AS last_name -- Last Name (string) 29 | , MAX(CASE WHEN KEY = 'twitterhandle' THEN value END) AS twitter_handle -- Twitter Username (string) 30 | , MAX(CASE WHEN KEY = 'city' THEN value END) AS city -- City (string) 31 | , MAX(CASE WHEN KEY = 'state' THEN value END) AS "state" -- State/Region (string) 32 | , MAX(CASE WHEN KEY = 'jobtitle' THEN value END) AS job_title -- Job Title (string) 33 | , MAX(CASE WHEN KEY = 'company' THEN value END) AS company -- Company Name (string) 34 | , MAX(CASE WHEN KEY = 'website' THEN value END) AS website -- Website URL (string) 35 | -- Dates and Durations 36 | , MAX(CASE WHEN KEY = 'createdate' THEN TIMESTAMP 'epoch' + value / 1000 * interval '1 second' END) AS created_date -- Create Date (timestamp) 37 | , MAX(CASE WHEN KEY = 'lastmodifieddate' THEN TIMESTAMP 'epoch' + value / 1000 * interval '1 second' END) AS last_modified_date -- Last Modified Date (timestamp) 38 | , MAX(CASE WHEN KEY = 'hs_lifecyclestage_lead_date' THEN TIMESTAMP 'epoch' + value / 1000 * interval '1 second' END) AS lifecyclestage_lead_date -- Became a Lead Date (timestamp) 39 | , MAX(CASE WHEN KEY = 'hs_analytics_first_timestamp' THEN TIMESTAMP 'epoch' + value / 1000 * interval '1 second' END) AS first_timestamp -- Time First Seen (timestamp) 40 | , MAX(CASE WHEN KEY = 'hs_analytics_last_timestamp' THEN TIMESTAMP 'epoch' + value / 1000 * interval '1 second' END) AS last_timestamp -- Time Last Seen (timestamp) 41 | , MAX(CASE WHEN KEY = 'hs_analytics_first_visit_timestamp' THEN TIMESTAMP 'epoch' + value / 1000 * interval '1 second' END) AS first_visit_timestamp -- Time of First Session (timestamp) 42 | , MAX(CASE WHEN KEY = 'hs_analytics_last_visit_timestamp' THEN TIMESTAMP 'epoch' + value / 1000 * interval '1 second' END) AS last_visit_timestamp -- Time of Last Session (timestamp) 43 | -- Sales Pipeline Info 44 | , MAX(CASE WHEN KEY = 'lifecyclestage' THEN value END) AS lifecycle_stage -- Lifecycle Stage (enumeration) 45 | , MAX(CASE WHEN KEY = 'hs_email_optout' THEN value END) AS email_optout -- Unsubscribed from all email (bool) 46 | , MAX(CASE WHEN KEY = 'hs_email_quarantined' THEN value END) AS email_quarantined -- Email Address Quarantined (bool) 47 | , MAX(CASE WHEN KEY = 'hs_all_contact_vids' THEN value END) AS all_contact_vids -- All vids for a contact (enumeration) 48 | -- Money 49 | , MAX(CASE WHEN KEY = 'hs_analytics_revenue' THEN value END) AS revenue -- Event Revenue (number) 50 | -- Numbers 51 | , MAX(CASE WHEN KEY = 'hs_analytics_average_page_views' THEN value END) AS average_page_views -- Average Pageviews (number) 52 | , MAX(CASE WHEN KEY = 'num_conversion_events' THEN value END) AS num_conversion_events -- Number of Form Submissions (number) 53 | , MAX(CASE WHEN KEY = 'num_unique_conversion_events' THEN value END) AS num_unique_conversion_events -- Number of Unique Forms Submitted (number) 54 | , MAX(CASE WHEN KEY = 'hs_analytics_num_page_views' THEN value END) AS num_page_views -- Number of Pageviews (number) 55 | , MAX(CASE WHEN KEY = 'hs_analytics_num_visits' THEN value END) AS num_visits -- Number of Sessions (number) 56 | , MAX(CASE WHEN KEY = 'hs_analytics_num_event_completions' THEN value END) AS num_event_completions -- Number of event completions (number) 57 | -- Data Source Fields 58 | , MAX(CASE WHEN KEY = 'hs_analytics_source' THEN value END) AS source -- Original Source (enumeration) 59 | , MAX(CASE WHEN KEY = 'hs_analytics_source_data_1' THEN value END) AS source_data_1 -- Original Source Drill-Down 1 (string) 60 | , MAX(CASE WHEN KEY = 'hs_analytics_source_data_2' THEN value END) AS source_data_2 -- Original Source Drill-Down 2 (string) 61 | FROM hubspot_contacts_properties p 62 | GROUP BY p.hubspot_contacts_id 63 | ) cp ON cp.contact_id = hc.contact_id 64 | LEFT JOIN ( 65 | SELECT DISTINCT hcmv.hubspot_contacts_id AS merged_contact_id 66 | , "value" AS merged_vid 67 | , hcma."first-name" AS merged_contact_first_name 68 | , hcma."last-name" AS merged_contact_last_name 69 | , hcma."num-properties-moved" AS merged_contact_properties_moved 70 | FROM "hubspot_contacts_merged-vids" hcmv 71 | INNER JOIN "hubspot_contacts_merge-audits" hcma ON hcma.hubspot_contacts_id = hcmv.hubspot_contacts_id 72 | WHERE "value" <> hcmv.hubspot_contacts_id 73 | ) hcm ON hcm.merged_contact_id = hc.contact_id; 74 | ``` 75 | -------------------------------------------------------------------------------- /hubspot/views/deals_companies.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: Deals Companies 3 | description: One to many connection table between all the companies to their respective deals 4 | usage: This view can be used in order to connect between a company and its deals 5 | --- 6 | 7 | ```sql 8 | -- CREATE VIEW hubspot_deals_companies AS 9 | SELECT hda.hubspot_deals_id AS deal_id 10 | , hdaac.value AS company_id 11 | FROM hubspot_deals_associations hda 12 | LEFT JOIN hubspot_deals_associations_associatedcompanyids hdaac ON hda.id = hdaac.hubspot_deals_associations_id 13 | WHERE hdaac.value IS NOT NULL 14 | GROUP BY 1, 2; 15 | ``` 16 | -------------------------------------------------------------------------------- /hubspot/views/deals_consolidated.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: Deals consolidated 3 | description: Denormalized view of Hubspot's deals data. This view can be used together with other Hubspot views 4 | usage: This is the "ready for analysis" view of Hubspot deals 5 | modifications: Properties can be removed or added. To add custom properties, refer to your Hubspot properties setting for their name. Dates are formatted as Unix timestamps so they must be transformed and cast into dates. 6 | --- 7 | 8 | ```sql 9 | -- CREATE VIEW hubspot_deals_consolidated AS 10 | SELECT hdp.* 11 | FROM ( 12 | SELECT id AS deal_id 13 | FROM hubspot_deals 14 | WHERE isdeleted IS FALSE 15 | ) hd 16 | INNER JOIN ( 17 | SELECT hubspot_deals_id as deal_id 18 | -- Deal Details 19 | , MAX(CASE WHEN KEY = 'dealname' THEN value END) AS deal_name 20 | , MAX(CASE WHEN KEY = 'dealtype' THEN value END) AS deal_type 21 | , MAX(CASE WHEN KEY = 'dealstage' THEN value END) AS deal_stage 22 | , MAX(CASE WHEN KEY = 'description' THEN value END) AS description 23 | -- Dates and Durations 24 | , MAX(CASE WHEN KEY = 'createdate' THEN TIMESTAMP 'epoch' + value / 1000 * interval '1 second' END) AS created_date 25 | , MAX(CASE WHEN KEY = 'hs_date_entered_appointmentscheduled' THEN TIMESTAMP 'epoch' + value / 1000 * interval '1 second' END) AS date_entered_appointmentscheduled 26 | , MAX(CASE WHEN KEY = 'hs_date_exited_appointmentscheduled' THEN TIMESTAMP 'epoch' + value / 1000 * interval '1 second' END) AS date_exited_appointmentscheduled 27 | , MAX(CASE WHEN KEY = 'hs_time_in_appointmentscheduled' THEN EXTRACT(epoch from value / 1000 * interval '1 second') END) AS time_in_appointmentscheduled --type interval 28 | , MAX(CASE WHEN KEY = 'hs_date_entered_qualifiedtobuy' THEN TIMESTAMP 'epoch' + value / 1000 * interval '1 second' END) AS date_entered_qualifiedtobuy 29 | , MAX(CASE WHEN KEY = 'hs_date_exited_qualifiedtobuy' THEN TIMESTAMP 'epoch' + value / 1000 * interval '1 second' END) AS date_exited_qualifiedtobuy 30 | , MAX(CASE WHEN KEY = 'hs_time_in_qualifiedtobuy' THEN EXTRACT(epoch from value / 1000 * interval '1 second') END) AS time_in_qualifiedtobuy --type interval 31 | , MAX(CASE WHEN KEY = 'hs_date_entered_presentationscheduled' THEN TIMESTAMP 'epoch' + value / 1000 * interval '1 second' END) AS date_entered_presentationscheduled 32 | , MAX(CASE WHEN KEY = 'hs_date_exited_presentationscheduled' THEN TIMESTAMP 'epoch' + value / 1000 * interval '1 second' END) AS date_exited_presentationscheduled 33 | , MAX(CASE WHEN KEY = 'hs_time_in_presentationscheduled' THEN EXTRACT(epoch from value / 1000 * interval '1 second') END) AS time_in_presentationscheduled --type interval 34 | , MAX(CASE WHEN KEY = 'hs_date_entered_closedlost' THEN TIMESTAMP 'epoch' + value / 1000 * interval '1 second' END) AS date_entered_closedlost 35 | , MAX(CASE WHEN KEY = 'hs_time_in_closedlost' THEN EXTRACT(epoch from value / 1000 * interval '1 second') END) AS time_in_closedlost --type interval 36 | , MAX(CASE WHEN KEY = 'hs_date_entered_closedwon' THEN TIMESTAMP 'epoch' + value / 1000 * interval '1 second' END) AS date_entered_closedwon 37 | , MAX(CASE WHEN KEY = 'hs_time_in_closedwon' THEN EXTRACT(epoch from value / 1000 * interval '1 second') END) AS time_in_closedwon --type interval 38 | , MAX(CASE WHEN KEY = 'closedate' THEN TIMESTAMP 'epoch' + value / 1000 * interval '1 second' END) AS close_date 39 | , MAX(CASE WHEN KEY = 'days_to_close' THEN value END) AS days_to_close 40 | , MAX(CASE WHEN KEY = 'engagements_last_meeting_booked' THEN TIMESTAMP 'epoch' + value / 1000 * interval '1 second' END) AS last_meeting_booked 41 | , MAX(CASE WHEN KEY = 'hs_latest_meeting_activity' THEN TIMESTAMP 'epoch' + value / 1000 * interval '1 second' END) AS latest_meeting_activity 42 | , MAX(CASE WHEN KEY = 'hs_lastmodifieddate' THEN TIMESTAMP 'epoch' + value / 1000 * interval '1 second' END) AS last_modified_date 43 | , MAX(CASE WHEN KEY = 'hubspot_owner_assigneddate' THEN TIMESTAMP 'epoch' + value / 1000 * interval '1 second' END) AS owner_assigned_date 44 | , MAX(CASE WHEN KEY = 'hs_sales_email_last_replied' THEN TIMESTAMP 'epoch' + value / 1000 * interval '1 second' END) AS sales_email_last_replied 45 | , MAX(CASE WHEN KEY = 'notes_last_contacted' THEN TIMESTAMP 'epoch' + value / 1000 * interval '1 second' END) AS notes_last_contacted 46 | , MAX(CASE WHEN KEY = 'notes_last_updated' THEN TIMESTAMP 'epoch' + value / 1000 * interval '1 second' END) AS notes_last_updated 47 | , MAX(CASE WHEN KEY = 'notes_next_activity_date' THEN TIMESTAMP 'epoch' + value / 1000 * interval '1 second' END) AS notes_next_activity_date 48 | -- Sales Pipeline Info 49 | , MAX(CASE WHEN KEY = 'pipeline' THEN value END) AS pipeline 50 | , MAX(CASE WHEN KEY = 'hubspot_owner_id' THEN CAST(value as INT) END) AS owner_id 51 | , MAX(CASE WHEN KEY = 'hs_all_owner_ids' THEN value END) AS all_owner_ids 52 | , MAX(CASE WHEN KEY = 'closed_lost_reason' THEN value END) AS closedlost_reason 53 | , MAX(CASE WHEN KEY = 'closed_won_reason' THEN value END) AS closedwon_reason 54 | , MAX(CASE WHEN KEY = 'hs_is_closed' THEN value END) AS is_closed 55 | -- Money 56 | , MAX(CASE WHEN KEY = 'amount_in_home_currency' THEN value END) AS amount_in_home_currency 57 | , MAX(CASE WHEN KEY = 'amount' THEN value END) AS amount 58 | , MAX(CASE WHEN KEY = 'hs_closed_amount' THEN value END) AS closed_amount 59 | , MAX(CASE WHEN KEY = 'hs_closed_amount_in_home_currency' THEN value END) AS closed_amount_in_home_currency 60 | , MAX(CASE WHEN KEY = 'hs_projected_amount' THEN value END) AS projected_amount 61 | , MAX(CASE WHEN KEY = 'hs_projected_amount_in_home_currency' THEN value END) AS projected_amount_in_home_currency 62 | , MAX(CASE WHEN KEY = 'hs_forecast_amount' THEN value END) AS forecast_amount 63 | -- Probabilities 64 | , MAX(CASE WHEN KEY = 'hs_deal_stage_probability' THEN value END) AS deal_stage_probability 65 | , MAX(CASE WHEN KEY = 'hs_forecast_probability' THEN value END) AS forecast_probability 66 | , MAX(CASE WHEN KEY = 'hs_manual_forecast_category' THEN value END) AS manual_forecast_category 67 | -- Data Fields 68 | , MAX(CASE WHEN KEY = 'hs_analytics_source' THEN value END) AS source 69 | , MAX(CASE WHEN KEY = 'hs_analytics_source_data_1' THEN value END) AS source_data_1 70 | , MAX(CASE WHEN KEY = 'hs_analytics_source_data_2' THEN value END) AS source_data_2 71 | , MAX(CASE WHEN KEY = 'lead_source' THEN value END) AS lead_source 72 | , MAX(CASE WHEN KEY = 'lod' THEN value END) AS lod 73 | -- Numbers 74 | , MAX(CASE WHEN KEY = 'num_associated_contacts' THEN value END) AS num_associated_contacts 75 | , MAX(CASE WHEN KEY = 'num_contacted_notes' THEN value END) AS num_contacted_notes 76 | , MAX(CASE WHEN KEY = 'num_notes' THEN value END) AS num_notes 77 | FROM hubspot_deals_properties 78 | GROUP BY hubspot_deals_id 79 | ) hdp ON hdp.deal_id = hd.deal_id; 80 | ``` 81 | -------------------------------------------------------------------------------- /hubspot/views/deals_contacts.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: Deals Contacts 3 | description: Many to many connection table between all the contacts to their respective deals 4 | usage: This view can be used in order to connect between a contact and its deals 5 | --- 6 | 7 | ```sql 8 | -- CREATE VIEW hubspot_deals_contacts AS 9 | SELECT hda.hubspot_deals_id AS deal_id 10 | , hdaav.value AS contact_id 11 | FROM hubspot_deals_associations hda 12 | LEFT JOIN hubspot_deals_associations_associatedvids hdaav ON hda.id = hdaav.hubspot_deals_associations_id 13 | WHERE hdaav.value IS NOT NULL 14 | GROUP BY 1, 2; 15 | ``` 16 | -------------------------------------------------------------------------------- /hubspot/views/engagements_consolidated.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: Engagements consolidated 3 | description: Denormalized view of Hubspot's engagements data. This view can be used together with other Hubspot views 4 | usage: This is the "ready for analysis" view of Hubspot engagements 5 | modifications: Properties can be removed or added. To add custom properties, refer to your Hubspot properties setting for their name. 6 | --- 7 | 8 | ```sql 9 | -- CREATE VIEW hubspot_engagements_consolidated AS 10 | SELECT e.id AS engagement_id 11 | , e.ownerid AS owner_id 12 | , ecp.value AS company_id 13 | , ect.value AS contact_id 14 | , ed.value AS deal_id 15 | , e."type" 16 | , e."timestamp" AS engagement_timestamp 17 | FROM hubspot_engagements e 18 | LEFT JOIN hubspot_engagements_associations_companyids ecp ON e.id = ecp.hubspot_engagements_id 19 | LEFT JOIN hubspot_engagements_associations_contactids ect ON e.id = ect.hubspot_engagements_id 20 | LEFT JOIN hubspot_engagements_associations_dealids ed ON e.id = ed.hubspot_engagements_id 21 | WHERE case when e.active = 1 then TRUE when e.active = 0 then FALSE else e.active::bigint::boolean end IS TRUE; 22 | ``` 23 | -------------------------------------------------------------------------------- /hubspot/views/owners_consolidated.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: Owners consolidated 3 | description: Denormalized view of Hubspot's owners data. This view can be used together with other Hubspot views 4 | usage: This is the "ready for analysis" view of Hubspot owners 5 | modifications: Properties can be removed or added. To add custom properties, refer to your Hubspot properties setting for their name. Dates are formatted as Unix timestamps so they must be transformed and cast into dates. 6 | --- 7 | 8 | ```sql 9 | -- CREATE VIEW hubspot_owners_consolidated AS 10 | SELECT ownerid AS owner_id 11 | , type 12 | , firstname AS first_name 13 | , lastname AS last_name 14 | , email 15 | , TIMESTAMP 'epoch' + createdat / 1000 * interval '1 second' AS created_date 16 | , TIMESTAMP 'epoch' + updatedat / 1000 * interval '1 second' AS updated_date 17 | FROM hubspot_owners 18 | WHERE case when isactive = 1 then TRUE when isactive = 0 then FALSE else isactive::bigint::boolean end IS TRUE; 19 | ``` 20 | -------------------------------------------------------------------------------- /salesforce/queries/new_opps_summary.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: New Opportunities Summary 3 | description: This query shows different details about opportunities derived from Salesforce data. 4 | requirements: Collect the `Opportunity`, `User`, `Account`, `Contact` and `Lead` objects with the Panoply Salesforce data source and create the view `salesforce_account_to_email`, more details [here](https://github.com/panoplyio/sql-library/blob/master/salesforce/views/account_to_email.md). 5 | usage: This query can be displayed in a tabular form to display the current opportunities. 6 | modifications: The table in the `FROM` might need to be changed based on Schema and Destination settings in the data source. The Filters for `closedate, stagename, probability` in the `WHERE` clause can be changed or completely removed. 7 | --- 8 | 9 | ```sql 10 | SELECT 11 | sfu.name opp_owner, 12 | sfo.stagename stage, 13 | sfo.probability, 14 | sfa.name opp_name, 15 | sfae.email opp_email, 16 | sfo.amount, 17 | sfo.nextstep next_step, 18 | sfo.closedate close_date, 19 | sfo.createddate created_date 20 | FROM 21 | public.salesforce_opportunity sfo 22 | JOIN public.salesforce_user sfu ON sfo.ownerid = sfu.id 23 | LEFT JOIN public.salesforce_account sfa on sfa.id = sfo.accountid 24 | LEFT JOIN public.salesforce_account_to_email sfae ON sfa.id = sfae.accountid 25 | WHERE 26 | sfo.closedate between '2020-01-01' AND '2020-03-31' -- Dates can be changed \ Filter can be removed 27 | AND sfo.stagename in ('Identified Need', 'Trial', 'Contract Sent', 'Verbal Commit') -- Stage Names can be changed \ Filter can be removed 28 | AND sfo.probability > 10 -- Value can be changed \ Filter can be removed 29 | ORDER BY 30 | 1, 31 | 2, 32 | 3 DESC 33 | ``` 34 | 35 | ## Query Results Dictionary 36 | 37 | | Column | Description | 38 | | --- | --- | 39 | | `opp_owner`| Opportunity Owner (Sales Rep) | 40 | | `stage`| Opportunity Stage | 41 | | `probability`| Opportunity close probability | 42 | | `opp_name`| Opportunity Name | 43 | | `opp_email`| Opportunity Email | 44 | | `amount`| Deal Amount | 45 | | `next_step`| Next Step | 46 | | `close_date`| Close Date | 47 | | `created_date`| Created Date | 48 | -------------------------------------------------------------------------------- /salesforce/queries/revenue_and_loss_by_opportunity_type.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: All revenue and loss for the Quarter-To-Date (QTD) grouped by type of opportunity. 3 | description: This query sums the total amount from opportunities grouped by type. A total is displayed at the bottom 4 | requirements: Collect the `Opportunities` Resource with the Panoply Salesforce data source. 5 | usage: This query can be displayed in a pivot form to display the total amount per opportunity type. 6 | modifications: The table in the `FROM` might need to be changed based on Schema and Destination settings in the data source. The Date Range Filter using the `closedate` in the `WHERE` clause can be changed. Everything below the UNION ALL clause can also be removed if you prefer to not have the total at the bottom. 7 | --- 8 | 9 | ```sql 10 | SELECT 11 | "type", 12 | SUM("amount") AS "amount", 13 | COUNT(*) AS "opps" 14 | FROM 15 | public.salesforce_opportunity 16 | WHERE 17 | DATE_TRUNC('quarter', "closedate") = DATE_TRUNC('quarter', CURRENT_DATE) 18 | AND stagename = 'Closed Won' 19 | GROUP BY 20 | 1 21 | UNION ALL 22 | SELECT 23 | 'Total' AS "type", 24 | SUM("amount") AS "amount", 25 | COUNT(*) AS "opps" 26 | FROM 27 | public.salesforce_opportunity 28 | WHERE 29 | DATE_TRUNC('quarter', "closedate") = DATE_TRUNC('quarter', CURRENT_DATE) 30 | AND stagename = 'Closed Won' 31 | ORDER BY 32 | amount 33 | ``` 34 | 35 | ## Query Results Dictionary 36 | 37 | | Column | Description | 38 | |--- | --- | 39 | | `type` | Opportunity type | 40 | | `amount` | Sum of the opportunity amount | 41 | | `opps` | Opportunity count | 42 | -------------------------------------------------------------------------------- /salesforce/queries/sales_rep_conversion_rate_qtd.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: Sales Rep Lead-to-Opportunity Conversion 3 | description: This query shows the conversion count and rate of lead-to-opportunity conversion and closed opportunities per active sales rep derived from Salesforce data. 4 | requirements: Collect the `User`, `Opportunity`, and `Lead` objects with the Panoply Salesforce data source 5 | usage: This query can be displayed in a tabular form to display the count and rate per active sales rep 6 | modifications: The table in the `FROM` might need to be changed based on Schema and Destination settings in the data source. The Date Range Filter using the `createddate` in the `WHERE` clause can be changed. 7 | --- 8 | 9 | ```sql 10 | WITH active_user AS ( 11 | SELECT 12 | id, email, firstname || ' ' || lastname AS rep 13 | FROM 14 | public.salesforce_user 15 | WHERE 16 | salesforce_user.IsActive = 1 17 | ), 18 | converted_and_won AS ( 19 | SELECT 20 | sl.ownerid, 21 | COUNT(*) AS "total_leads", 22 | COUNT(CASE WHEN sl.convertedopportunityid IS NOT NULL THEN 1 END) AS "converted_leads", 23 | COUNT(CASE WHEN so.stagename = 'Closed Won' THEN 1 END) AS "won_leads", 24 | ROUND(converted_leads * 100.0 / total_leads, 2) AS "conversion_rate", 25 | ROUND(won_leads * 100.0 / total_leads, 2) AS "win_rate" 26 | FROM 27 | public.salesforce_lead sl 28 | LEFT JOIN 29 | public.salesforce_opportunity so 30 | ON sl.convertedopportunityid = so.id 31 | WHERE 32 | DATE_TRUNC('quarter', sl."createddate") = DATE_TRUNC('quarter', CURRENT_DATE) 33 | GROUP BY 34 | 1 35 | ) 36 | SELECT 37 | au.rep AS "sales_rep", 38 | au.email, 39 | srl.total_leads, 40 | srl.converted_leads, 41 | srl.won_leads, 42 | srl.conversion_rate, 43 | srl.win_rate 44 | FROM 45 | converted_and_won srl 46 | JOIN 47 | active_user au 48 | ON srl.ownerid = au.id 49 | ORDER BY 50 | total_leads desc 51 | ``` 52 | 53 | ## Query Results Dictionary 54 | 55 | | Column | Description | 56 | | --- | --- | 57 | | `sales_rep`| Opportunity Owner (Sales Rep) | 58 | | `email`| Sales Rep Email | 59 | | `total_leads`| Total Leads Owned by the Sales Rep | 60 | | `converted_leads`| Total Leads Converted to Opportunity by the Sales Rep | 61 | | `won_leads`| Total Opportunities moved to the Closed/Won stage | 62 | | `conversion_rate`| Rate of conversion from Lead to Opportunity | 63 | | `win_rate`| Rate of opportunities being closed/won | 64 | -------------------------------------------------------------------------------- /salesforce/queries/sales_rep_ranked_by_revenue.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: Sales Rep Ranked by Revenue for the Quarter-To-Date (QTD) 3 | description: This query shows the active sales reps ranked by revenue. Amount is tallied from closed and won opportunities by the sales reps derived from Salesforce data. 4 | requirements: Collect the `User` and `Opportunity` objects with the Panoply Salesforce data source 5 | usage: This query can be displayed in a tabular or pivot form to display the revenue per active sales rep 6 | modifications: The table in the `FROM` might need to be changed based on Schema and Destination settings in the data source. The Date Range Filter using the `closedate` in the `WHERE` clause can be changed. 7 | --- 8 | 9 | ```sql 10 | WITH salesforce_user AS ( 11 | SELECT 12 | id, email, firstname || ' ' || lastname AS "rep" 13 | FROM 14 | public.salesforce_user 15 | WHERE 16 | salesforce_user.IsActive 17 | ) 18 | SELECT 19 | us.rep AS "sales_rep", 20 | us.email, 21 | sum(op.amount) AS "revenue" 22 | FROM 23 | public.salesforce_opportunity op 24 | JOIN 25 | salesforce_user us 26 | ON op.ownerid = us.id 27 | WHERE 28 | op.stagename = 'Closed Won' 29 | AND DATE_TRUNC('quarter', "closedate") = DATE_TRUNC('quarter', current_date) 30 | GROUP BY 31 | 1, 32 | 2 33 | ORDER BY 34 | revenue DESC 35 | ``` 36 | 37 | ## Query Results Dictionary 38 | 39 | | Column | Description | 40 | | --- | --- | 41 | | `sales_rep`| Opportunity Owner (Sales Rep) | 42 | | `email`| Sales Rep Email | 43 | | `revenue`| Sum of amount from closed/won opportunities by sales rep. | 44 | -------------------------------------------------------------------------------- /salesforce/views/account_to_email.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: Account to Email Mapping 3 | description: View for getting the connection between Salesforce `accountid` to the relevant user `email`. 4 | requirements: Collect the `Account`, `Contact` and `Lead` objects with the Panoply Salesforce data source. 5 | usage: This view can be used in many different salesforce reports for a quick connection between `accountid` and the relevant `email`. 6 | modifications: The table in the `FROM` might need to be changed based on Schema and Destination settings in the data source. Different filters can be added throughout the query, either in the subquery or by adding a `WHERE` clause in the final query. 7 | --- 8 | 9 | ```sql 10 | -- CREATE VIEW salesforce_account_to_email AS 11 | WITH email_accountid AS ( 12 | SELECT 13 | DISTINCT sfc.email, 14 | sfc.accountid 15 | FROM 16 | public.salesforce_contact sfc 17 | UNION -- Leads and Contacts are combined by UNION because they are mutually exclusive groups of individuals 18 | SELECT 19 | DISTINCT sfl.email, 20 | sfl.convertedaccountid 21 | FROM 22 | public.salesforce_lead sfl 23 | ) 24 | SELECT 25 | DISTINCT eaid.email, 26 | eaid.accountid, 27 | sfa.name account_name 28 | FROM 29 | email_accountid eaid 30 | LEFT JOIN public.salesforce_account sfa ON eaid.accountid = sfa.id 31 | ``` 32 | 33 | ## View Results Dictionary 34 | 35 | | Column | Description | 36 | | --- | --- | 37 | | `email`| Opportunity Email | 38 | | `accountid`| Salesforce Account ID | 39 | | `account_name`| Account Name | 40 | 41 | ## Example: New Opportunities Summary 42 | 43 | [See Query Details Here](https://github.com/panoplyio/sql-library/blob/master/salesforce/queries/new_opps_summary.md) 44 | -------------------------------------------------------------------------------- /shopify/queries/customer_order_annual_overview.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: Customer Order Annual Overview 3 | description: This query shows an overview of a customer's orders by processing method, financial status, discount codes, and total discount derived from Shopify data. 4 | requirements: Collect the `Orders` Resource with the Panoply Shopify data source. This will create sub-tables for the `Customer` and `Discount Codes` data. 5 | usage: This query can be displayed in a tabular form to display the order pattern of the customer. 6 | modifications: The table in the `FROM` might need to be changed based on Schema and Destination settings in the data source. The Date Range Filter using the `created_at` in the `WHERE` clause can be changed. 7 | --- 8 | 9 | ```sql 10 | SELECT 11 | soc.first_name || ' ' || soc.last_name AS "customer", 12 | soc.email, 13 | SUM(so.total_discounts) AS "total_discounts", 14 | LISTAGG(distinct sodc.code, ', ') AS "discount_codes", 15 | COUNT(*) AS "total_orders", 16 | COUNT(CASE WHEN so.processing_method = 'checkout' THEN 1 END) AS "checkout", 17 | COUNT(CASE WHEN so.processing_method = 'direct' THEN 1 END) AS "direct", 18 | COUNT(CASE WHEN so.processing_method = 'manual' THEN 1 END) AS "manual", 19 | COUNT(CASE WHEN so.processing_method = 'offsite' THEN 1 END) AS "offsite", 20 | COUNT(CASE WHEN so.processing_method = 'express' THEN 1 END) AS "express", 21 | COUNT(CASE WHEN so.processing_method = 'free' THEN 1 END) AS "free", 22 | COUNT(CASE WHEN so.financial_status = 'pending' THEN 1 END) AS "pending", 23 | COUNT(CASE WHEN so.financial_status = 'authorized' THEN 1 END) AS "authorized", 24 | COUNT(CASE WHEN so.financial_status = 'partially_paid' THEN 1 END) AS "partially_paid", 25 | COUNT(CASE WHEN so.financial_status = 'paid' THEN 1 END) AS "paid", 26 | COUNT(CASE WHEN so.financial_status = 'partially_refunded' THEN 1 END) AS "partially_refunded", 27 | COUNT(CASE WHEN so.financial_status = 'refunded' THEN 1 END) AS "refunded", 28 | COUNT(CASE WHEN so.financial_status = 'voided' THEN 1 END) AS "voided" 29 | FROM 30 | public.shopify_orders so 31 | LEFT JOIN 32 | public.shopify_orders_customer soc 33 | ON soc.shopify_orders_id = so.id 34 | LEFT JOIN 35 | public.shopify_orders_discount_codes sodc 36 | ON sodc.shopify_orders_id = so.id 37 | WHERE 38 | DATE_TRUNC('year', so."created_at") = DATE_TRUNC('year', CURRENT_DATE) 39 | AND customer IS NOT NULL 40 | AND soc.email IS NOT NULL 41 | GROUP BY 42 | 1, 43 | 2 44 | ORDER BY 45 | total_orders DESC 46 | ``` 47 | 48 | ## Query Results Dictionary 49 | 50 | | Column | Description | 51 | | --- | --- | 52 | | `customer`| Customer's Full Name | 53 | | `email`| Customer's email | 54 | | `total_discounts`| total discount amount from all orders | 55 | | `discount_codes`| comma-separated list of all discount codes used | 56 | | `total_orders`| total order count | 57 | | `checkout`| Order Count under the processing method "checkout" | 58 | | `direct`| Order Count under the processing method "direct" | 59 | | `manual`| Order Count under the processing method "manual" | 60 | | `offsite`| Order Count under the processing method "offsite" | 61 | | `express`| Order Count under the processing method "express" | 62 | | `free`| Order Count with the financial status "free" | 63 | | `pending`| Order Count with the financial status "pending" | 64 | | `authorized`| Order Count with the financial status "authorized" | 65 | | `partially_paid`| Order Count with the financial status "partially_paid" | 66 | | `paid`| Order Count with the financial status "paid" | 67 | | `partially_refunded`| Order Count with the financial status "partially_refunded" | 68 | | `refunded`| Order Count with the financial status "refunded" | 69 | | `voided`| Order Count with the financial status "voided" | 70 | -------------------------------------------------------------------------------- /shopify/queries/order_count_by_processing_method.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: Order Analytics by Processing Method 3 | description: This query shows an overview of how many orders by financial status there are per processing method. 4 | requirements: Collect the `Orders` Resource with the Panoply Shopify data source. 5 | usage: This query can be displayed in a pivot form to display how many orders by financial status there are per processing method. 6 | modifications: The table in the `FROM` might need to be changed based on Schema and Destination settings in the data source. The Date Range Filter using the `created_at` in the `WHERE` clause can be changed. 7 | --- 8 | 9 | ```sql 10 | SELECT 11 | so.processing_method, 12 | COUNT(CASE WHEN so.financial_status = 'pending' THEN 1 END) AS "pending", 13 | COUNT(CASE WHEN so.financial_status = 'authorized' THEN 1 END) AS "authorized", 14 | COUNT(CASE WHEN so.financial_status = 'partially_paid' THEN 1 END) AS "partially_paid", 15 | COUNT(CASE WHEN so.financial_status = 'paid' THEN 1 END) AS "paid", 16 | COUNT(CASE WHEN so.financial_status = 'partially_refunded' THEN 1 END) AS "partially_refunded", 17 | COUNT(CASE WHEN so.financial_status = 'refunded' THEN 1 END) AS "refunded", 18 | COUNT(CASE WHEN so.financial_status = 'voided' THEN 1 END) AS "voided", 19 | COUNT(*) AS total_orders 20 | FROM 21 | public.shopify_orders so 22 | WHERE 23 | DATE_TRUNC('quarter', so."created_at") = DATE_TRUNC('quarter', CURRENT_DATE) 24 | GROUP BY 25 | 1 26 | ORDER BY 27 | total_orders DESC 28 | ``` 29 | 30 | ## Query Results Dictionary 31 | 32 | | Column | Description | 33 | | --- | --- | 34 | | `processing_method`| Processing methods used by the orders | 35 | | `pending`| Order Count with the financial status "pending" | 36 | | `authorized`| Order Count with the financial status "authorized" | 37 | | `partially_paid`| Order Count with the financial status "partially_paid" | 38 | | `paid`| Order Count with the financial status "paid" | 39 | | `partially_refunded`| Order Count with the financial status "partially_refunded" | 40 | | `refunded`| Order Count with the financial status "refunded" | 41 | | `voided`| Order Count with the financial status "voided" | 42 | -------------------------------------------------------------------------------- /zendesk/queries/ticket_count_per_status_by_paying_customer.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: Ticket Count For Existing Customers 3 | description: This query shows how many zendesk tickets per status a paying customer has. Paying customers are derived from Salesforce data and the ticket count is derived from the Zendesk data. User data from the two datasources are linked by email. 4 | requirements: Collect the `Lead` and `Opportunity` objects with the Panoply Salesforce data source and also the `Users`, `Organizations`, and `Tickets` Resources form the Zendesk data source. 5 | usage: This query can be displayed in a tabular or pivot form to display the ticket count per status. 6 | modifications: The table in the `FROM` might need to be changed based on Schema and Destination settings in the data source. The Date Range Filter using the `created_at` in the `WHERE` clause can be changed. 7 | --- 8 | 9 | ```sql 10 | WITH paying_users AS ( 11 | SELECT 12 | DISTINCT sfl."email" 13 | FROM 14 | public.salesforce_lead sfl 15 | JOIN 16 | public.salesforce_opportunity sfo 17 | ON sfl."convertedopportunityid" = sfo."id" 18 | WHERE sfo.stagename = 'Closed Won' 19 | ), 20 | users AS ( 21 | SELECT 22 | zu."id", 23 | zu."name", 24 | zu."email", 25 | CASE 26 | WHEN zo."name" IS NULL THEN zu."email" 27 | ELSE zo."name" 28 | END AS requester 29 | FROM 30 | public.zendesk_users zu 31 | LEFT JOIN 32 | public.zendesk_organizations zo 33 | ON zu."organization_id" = zo."id" 34 | JOIN 35 | public.paying_users apu 36 | ON apzu."email" = zu."email" 37 | ) 38 | SELECT 39 | u."name", 40 | u."requester", 41 | COUNT(CASE WHEN zt."status" = 'new' THEN 1 END) AS "new", 42 | COUNT(CASE WHEN zt."status" = 'open' THEN 1 END) AS "open", 43 | COUNT(CASE WHEN zt."status" = 'pending' THEN 1 END) AS "pending", 44 | COUNT(CASE WHEN zt."status" = 'hold' THEN 1 END) AS "hold", 45 | COUNT(CASE WHEN zt."status" = 'solved' THEN 1 END) AS "solved", 46 | COUNT(CASE WHEN zt."status" = 'closed' THEN 1 END) AS "closed", 47 | COUNT(*) AS "total" 48 | FROM 49 | public.zendesk_tickets zt 50 | JOIN 51 | users u 52 | ON zt."submitter_id" = u."id" 53 | WHERE 54 | DATE_TRUNC('quarter', zt."created_at") = DATE_TRUNC('quarter', CURRENT_DATE) 55 | GROUP BY 56 | 1, 57 | 2 58 | ORDER BY 59 | total DESC 60 | ``` 61 | 62 | ## Query Results Dictionary 63 | 64 | | Column | Description | 65 | | --- | --- | 66 | | `name`| Name of Zendesk User | 67 | | `requester`| Zendesk Organization or the Zendesk User Email if organization is not set. | 68 | | `new`| Zendesk tickets count under the "new" status | 69 | | `open`| Zendesk tickets count under the "open" status | 70 | | `pending`| Zendesk tickets count under the "pending" status | 71 | | `hold`| Zendesk tickets count under the "hold" status | 72 | | `solved`| Zendesk tickets count under the "solved" status | 73 | | `closed`| Zendesk tickets count under the "closed" status | 74 | | `total`| Total tickets count | 75 | --------------------------------------------------------------------------------