Build queries¶
A query is SQL that runs from the Queries page against database tables in the Customer 360 page. A query returns a refined and filtered subset of useful customer data.
Queries page¶
The Queries page provides an overview of the status of every query. A table shows the status and details. Queries are listed by row. The details include the date and time at which this query last ran, along with the number of records (rows) that were returned during the last completed run.

Query editor¶
The SQL Query Editor is the user interface for a full SQL query engine based on Presto SQL that interacts with customer database tables in Amperity. The SQL Query Editor relies primarily on using the SELECT statement, along with common table expressions, joins, functions, and other parts of Presto SQL to build and design advanced queries.
Use the Query Editor to build SQL queries against tables and columns in your customer 360 database to support any downstream workflow. The Query Editor uses Presto SQL.

Queries may be authored using the visual Query Editor or the SQL Query Editor. Click Create, and then select the query editor to open. Queries that are already created have an icon that shows from which query editor they were authored.
indicates the query was created using the visual Query Editor.
indicates the query was created using the SQL Query Editor.
All queries must be activated before they can run as part of a scheduled workflow.
Note
Amperity is a multi-user system and the set of queries for your company is shared across all users. That means that if one user creates a draft query, another can open and edit it, so work can be easily passed between people on your team.
However, it also means that if 2 users are editing the same thing at the same time, their changes will collide. Amperity resolves this by applying the last set of changes saved as a whole. This will always keep the query in a consistent state (it will never be half-implemented). But changes that were saved first will be overwritten. As a result, we strongly recommend that you coordinate changes to specific objects in Amperity with others on your team.
About Presto SQL¶
Presto is a distributed SQL query engine designed to efficiently query large amounts of data using distributed queries. The Queries and Segments pages use Presto to return query results and audience segments.
Amperity queries are built using Presto SQL to define a SELECT statement. Please refer to the Presto SQL reference.
All queries that are built via the SQL Query Editor are done using a SELECT statement. In some cases, a WITH is used along with the SELECT statement. Each select statement can additional functionality, such as WHERE, LEFT JOIN, GROUP BY, ORDER BY, LIMIT clauses, CASE expressions, functions, operators, and other components that are part of Presto SQL, which is the underlying SQL engine for both the visual Query Editor and SQL Query Editor.
Tip
Follow the recommendations and patterns for indentation, naming conventions, reserved words, and whitespace.
Build queries¶
Queries may be authored using the visual Query Editor or the SQL Query Editor. Click Create and then select the query editor to open. Queries that are already created have an icon that shows from which query editor they were authored. All queries must be activated before they can run as part of a scheduled workflow.
Add query¶
The SQL Query Editor is the user interface for a full SQL query engine based on Presto SQL that interacts with customer database tables in Amperity. The SQL Query Editor relies primarily on using the SELECT statement, along with common table expressions, joins, functions, and other parts of Presto SQL to build and design advanced queries.
To add a query using the SQL Query Editor
From the Queries page click Create, and then select SQL Query. This opens the SQL Query Editor.
Under Database, select a database. The Customer 360 database is selected by default.
Define the query against the selected database.
Click Run Query and debug any issues that may arise.
Click Activate.
Preview results¶
You can preview the results of a SQL query by clicking the Run Query button. This will do one of the following things:
Return the first 100 results of the query to the preview pane directly below the query editor.
Return an empty table.
Return some type of error.
Use the preview results pane to fine-tune your queries, to make sure they return the data you want, and to make sure they don’t contain any errors.
Tip
Query editor SQL queries often evaluate millions of records. This means they may take a few minutes to run. You may use other areas of Amperity while a query is being run.
For draft queries, setting a the LIMIT clause to “100” while developing a query is often enough for testing and validating query results against very large data sets.
To preview query results
From the Queries page, open the menu for a query, and then select View. This opens a query editor.
Click Run Query to run the query. Wait for it to return results.
Example the columns and the data that is in them.
Adjust your query as necessary until it runs correctly.
Click Activate.
Validate query¶
SQL queries are validated from the SQL Query Editor by clicking the Run Query button. The results of the query are returned in the results window. The quality of the results can be inspected, and then fine-tuned. Errors in the syntax are reported in the results window.
Add to orchestration¶
Use the Orchestration option to define a schedule for a query.
To add a query to an orchestration
From the Queries page, open the menu for a query, and then select View. This opens a query editor.
Tip
The query does not need to be in edit mode to configure an orchestration.
Under Being Sent To click Add one now. This opens the Add Orchestration dialog box.
Follow the steps to add an orchestration. The steps will vary depending on the destination, the data template, and the orchestration.
Click Save.
Example queries¶
This section contains examples of queries that you can add to your tenant:
Important
These queries are not meant to be copy/paste queries. Use them as examples. Most will require some customization to be used effectively within your tenant.
Cohort analysis¶
The following SQL builds a cohort analysis against the Transaction Attributes Extended table that returns a month-by-month view of customers acquired, split by channel (store vs. online), and then for each monthly cohort, how many repurchased within 60, 90, 180, and 365 days, and the channel on which customers made their repeat purchases.
Tip
Build cohort analysis queries for your tenant, and then send the results downstream to your favorite analytics and/or BI tool.
1SELECT
2 YEAR(tae.first_order_datetime) AS first_order_year
3 ,MONTH(tae.first_order_datetime) AS first_order_month
4 ,tae.first_order_purchase_channel
5 ,COUNT(*) AS num_amp_id
6 ,SUM(CASE
7 WHEN tae.second_order_datetime <= tae.first_order_datetime + interval '60' day
8 THEN 1
9 ELSE 0
10 END) AS repeat_60d
11 ,SUM(CASE
12 WHEN tae.second_order_purchase_channel = 'web'
13 AND tae.second_order_datetime <= tae.first_order_datetime + interval '60' day
14 THEN 1
15 ELSE 0
16 END) AS repeat_60d_web
17 ,SUM(CASE
18 WHEN tae.second_order_purchase_channel = 'store'
19 AND tae.second_order_datetime <= tae.first_order_datetime + interval '60' day
20 THEN 1
21 ELSE 0
22 END) AS repeat_60d_store
23 ,SUM(CASE
24 WHEN tae.second_order_purchase_channel = 'web'
25 AND tae.second_order_datetime <= tae.first_order_datetime + interval '90' day
26 THEN 1
27 ELSE 0
28 END) AS repeat_90d_web
29 ,SUM(CASE
30 WHEN tae.second_order_purchase_channel = 'store'
31 AND tae.second_order_datetime <= tae.first_order_datetime + interval '90' day
32 THEN 1
33 ELSE 0
34 END) AS repeat_90d_store
35 ,SUM(CASE
36 WHEN tae.second_order_purchase_channel = 'web'
37 AND tae.second_order_datetime <= tae.first_order_datetime + interval '180' day
38 THEN 1
39 ELSE 0
40 END) AS repeat_180d_web
41 ,SUM(CASE
42 WHEN tae.second_order_purchase_channel = 'store'
43 AND tae.second_order_datetime <= tae.first_order_datetime + interval '180' day
44 THEN 1
45 ELSE 0
46 END) AS repeat_180d_store
47 ,SUM(CASE
48 WHEN tae.second_order_purchase_channel = 'web'
49 AND tae.second_order_datetime <= tae.first_order_datetime + interval '365' day
50 THEN 1
51 ELSE 0
52 END) AS repeat_365d_web
53 ,SUM(CASE
54 WHEN tae.second_order_purchase_channel = 'store'
55 AND tae.second_order_datetime <= tae.first_order_datetime + interval '365' day
56 THEN 1
57 ELSE 0
58 END) AS repeat_365d_store
59FROM Transaction_Attributes_Extended tae
60GROUP BY 1,2,3
61ORDER BY 1,2,3
Count loyalty by state¶
The following example counts customers in the United States, and then also in California, Oregon, Washington, Alaska, and Hawaii who also belong to the loyalty program (which is indicated when loyalty_id is not NULL):
1SELECT
2 state
3 ,COUNT(amperity_id) AS TotalCustomers
4FROM
5 Customer360
6WHERE (UPPER("country") = 'US'
7AND UPPER("state") in ('AK', 'CA', 'HI', 'OR', 'WA')
8AND LOWER("loyalty_id") IS NOT NULL)
9GROUP BY state
Customer acquisition¶
The following examples show how to track customer acquisition by day for single- and multi-brand tenants.
Tip
Build customer acquisition queries for your tenant, and then send the results downstream to your favorite analytics and/or BI tool.
For single-brand tenants
1SELECT
2 DATE(first_order_datetime) AS first_order_date
3 ,COUNT(DISTINCT amperity_id) AS total_customers
4 ,SUM(CASE
5 WHEN one_and_done
6 THEN 1
7 ELSE 0
8 END) AS total_one_and_done
9 ,AVG(lifetime_order_revenue) AS avg_clv
10 ,SUM(CASE
11 WHEN L12M_order_frequency > 0
12 THEN 1
13 ELSE 0
14 END) AS L12M_total_orders
15FROM Transaction_Attributes_Extended
16GROUP BY 1,2,3
17ORDER BY 1,2,3
For multi-brand tenants
1SELECT
2 multi_purchase_brand
3 ,multi_purchase_channel
4 ,DATE(first_order_datetime) AS first_order_date
5 ,COUNT(DISTINCT amperity_id) AS total_customers
6 ,SUM(CASE
7 WHEN one_and_done
8 THEN 1
9 ELSE 0
10 END) AS total_one_and_done
11 ,AVG(lifetime_order_revenue) AS avg_clv
12 ,SUM(CASE
13 WHEN L12M_order_frequency > 0
14 THEN 1
15 ELSE 0
16 END) AS L12M_total_orders
17FROM Transaction_Attributes_Extended
18GROUP BY 1,2,3
19ORDER BY 1,2,3
Days to second purchase¶
The following example shows how to return the days to second purchase starting from a date range for the first order.
1SELECT
2 days_to_second_purchase
3 ,COUNT(DISTINCT amperity_id) AS "customer_count"
4FROM (
5 SELECT
6 amperity_id
7 ,first_order_datetime
8 ,second_order_datetime
9 ,DATE_DIFF('day', first_order_datetime, second_order_datetime) AS "days_to_second_purchase"
10 FROM Transaction_Attributes_Extended
11 WHERE (
12 (
13 "amperity_id" IN (
14 SELECT DISTINCT "t0"."amperity_id"
15 FROM "Transaction_Attributes_Extended" "t0"
16 WHERE (
17 "t0"."first_order_datetime" BETWEEN TIMESTAMP '2019-01-01'
18 AND TIMESTAMP '2022-01-01'
19 AND "t0"."second_order_datetime" IS NOT NULL
20 )
21 )
22 )
23 )
24 GROUP BY amperity_id, first_order_datetime, second_order_datetime
25)
26GROUP BY 1
27ORDER BY 1 ASC
Tip
You can extend the WHERE statement to show days to second purchase by brand:
1WHERE (
2 "t0"."first_order_datetime" BETWEEN timestamp '2019-12-01'
3 AND timestamp '2023-12-28'
4 AND "t0"."second_order_datetime" IS NOT NULL
5 AND "t0"."first_order_purchase_brand" = 'Brand A'
6 --AND "t0"."first_order_purchase_brand" = 'Brand B'
7 --AND "t0"."first_order_purchase_brand" = 'Brand C'
8 --AND "t0"."first_order_purchase_brand" = 'Brand ...'
9 --Comment brands out when not needed
10)
Google Analytics reports¶
The following example shows how build acquisition channel reports using the GoogleAnalytics4_TransactionalAnalytics4 table that is created by the Google Analytics data source.
1WITH first_order AS (
2 SELECT DISTINCT
3 ut.amperity_id
4 ,order_ID
5 ,first_order_datetime
6 FROM Unified_Transactions ut
7 INNER JOIN (
8 SELECT
9 amperity_id
10 ,first_order_datetime
11 FROM Transaction_Attributes_Extended
12 WHERE first_order_datetime
13 BETWEEN timestamp '2022-09-05 00:00:00'
14 AND timestamp '2022-09-13 00:00:00') ta
15 ON ta.amperity_id = ut.amperity_id
16 AND ta.first_order_datetime = ut.order_datetime
17)
18
19SELECT
20 sessionSource
21 ,COUNT(amperity_id) new_customers
22FROM first_order
23LEFT JOIN (
24 SELECT
25 transactionId
26 ,sessionSource
27 FROM GoogleAnalytics4_TransactionalAnalytics4
28) GA
29ON first_order.order_ID = GA.transactionId
30GROUP BY 1
31ORDER BY 2 DESC
What columns are available?
You can use any column in the GoogleAnalytics4_TransactionalAnalytics4 table to build reports:
itemRevenue
transactionId
browser
sessionCampaignName
sessionSource
sessionGoogleAdsAdGroupName
date
deviceCategory
operatingSystem
sessionGoogleAdsQuery
operatingSystemVersion
sessionMedium
sessionGoogleAdsAdGroupId
Leaky bucket ratio¶
A leaky bucket ratio tracks the difference between the customers your brand wins and loses over time.
The following query shows how to build a leaky bucket ratio by using columns in the Customer Attributes and Transaction Attributes Extended tables to find the difference between lost and won customers during the past month and the past year.
Note
This example assumes that the Churn Trigger Start Datetime and Churn Trigger fields are enabled in the Customer Attributes table. Uncomment the following sections in the SQL for the Customer Attributes table:
1/*
2,churn_triggers_cte AS (
3 SELECT
4 amperity_id
5 ,concat(current_event_status,' trigger') AS churn_trigger
6 ,event_status_start_datetime AS churn_trigger_start_datetime
7 FROM churn_triggers
8)
9*/
and:
-- LEFT JOIN churn_triggers_cte ct ON ct.amperity_id = cl.amperity_id
1WITH leaky_bucket AS (
2
3 SELECT
4 YEAR(churn_trigger_start_datetime) AS year
5 ,MONTH(churn_trigger_start_datetime) AS month
6 ,SUM(IF(churn_trigger = 'Lost',1,0)) AS lost
7 ,0 AS won
8 FROM Customer_Attributes
9 GROUP BY 1,2
10
11 UNION ALL
12
13 SELECT
14 YEAR(first_order_datetime) AS year
15 ,MONTH(first_order_datetime) AS month
16 ,0 AS lost
17 ,COUNT(first_order_id) AS won
18 FROM Transaction_Attributes_Extended
19 GROUP BY 1,2
20)
21
22SELECT
23 year
24 ,month
25 ,SUM(lost) AS lost
26 ,SUM(won) AS won
27 ,SUM(won) - SUM(lost) AS ratio
28FROM leaky_bucket
29GROUP BY 1,2
30ORDER BY year DESC, month DESC
Missing Amperity IDs¶
Use the following example to find missing transaction IDs. The query calculates the percentage of missing Amperity IDs and customer IDs in the Unified Transactions table.
1SELECT
2 COUNT(DISTINCT CASE WHEN amperity_id IS NULL THEN order_id END) orders_missing_amp_id
3 ,COUNT(DISTINCT CASE WHEN customer_id IS NULL THEN order_id END) orders_missing_cust_id
4 ,1.0000*COUNT(DISTINCT CASE WHEN amperity_id IS NULL THEN order_id END)/COUNT(distinct order_id) pct_orders_missing_amp_id
5 ,1.0000*COUNT(DISTINCT CASE WHEN customer_id IS NULL THEN order_id END)/COUNT(distinct order_id) pct_orders_missing_cust_id
6FROM (
7 SELECT DISTINCT
8 ,order_id
9 ,amperity_id
10 ,customer_id AS customer_id_value
11 FROM Unified_Transactions
12)
Update the following line and replace “customer_id_value” with an actual customer ID:
customer_id AS customer_id_value
Month-over-month revenue¶
The following query returns month-over-month revenue.
1SELECT
2 MONTH(order_datetime) order_month
3 ,COUNT(DISTINCT ut.amperity_id) number_customers
4 ,COUNT(DISTINCT new_cust.amperity_id) number_new_customers
5 ,CAST(AVG(order_revenue) AS decimal) aov
6 ,1.00*SUM(order_revenue)/SUM(order_quantity) aor
7 ,CAST(SUM(order_revenue) AS DECIMAL) total_revenue
8FROM (
9 SELECT
10 amperity_id
11 ,order_datetime
12 ,SUM(item_revenue) order_revenue
13 ,SUM(item_quantity) order_quantity
14 FROM Unified_Itemized_Transactions
15 WHERE YEAR(order_datetime)=2020
16 GROUP BY 1,2) ut
17LEFT JOIN (
18 SELECT amperity_id
19 FROM Transaction_Attributes_Extended
20 WHERE year(first_order_datetime)=2020) new_cust
21ON ut.amperity_id=new_cust.amperity_id
22GROUP BY 1
One and dones, by category¶
The following example shows how to return all one-and-done purchasers for a single calendar year by product category:
1WITH new_in_21 AS (
2 SELECT
3 amperity_id
4 ,one_and_done
5 FROM Transaction_Attributes_Extended
6 WHERE YEAR(first_order_datetime) = 2021
7),
8
9product_categories AS (
10 SELECT DISTINCT
11 new_in_21.amperity_id
12 ,one_and_done
13 ,product_category
14 FROM Unified_Itemized_Transactions uit
15 INNER JOIN new_in_21 ON new_in_21.amperity_id=uit.amperity_id
16)
17
18SELECT
19 product_category
20 ,COUNT(distinct amperity_id) customer_count
21 ,1.0000*SUM(CASE when one_and_done THEN 1 ELSE 0 END) / COUNT(DISTINCT amperity_id) pct_one_done
22FROM product_categories
23GROUP BY 1
24ORDER BY 3 DESC
One and dones, by year¶
The following example shows how to return all one-and-done purchasers for a single calendar year:
1WITH one_and_dones_2022 AS (
2 SELECT
3 amperity_id
4 FROM Transaction_Attributes_Extended
5 WHERE one_and_done AND YEAR(first_order_datetime) = 2022
6)
7
8SELECT
9 COUNT(*) one_and_dones_2022
10FROM
11 one_and_dones_2022
Order revenue, 7-day rolling window¶
The following example shows a rolling seven day window for order revenue.
1SELECT
2 *
3FROM (
4 SELECT
5 purchase_channel
6 ,order_day
7 ,SUM(order_revenue) OVER (PARTITION BY purchase_channel ORDER BY order_day ROWS BETWEEN 6 preceding AND current row) rolling_7_day_revenue
8 FROM (
9 SELECT
10 purchase_channel
11 ,DATE(order_datetime) order_day
12 ,SUM(order_revenue) order_revenue
13 FROM Unified_Transactions
14 WHERE amperity_id IS NOT NULL
15 AND order_datetime > (CURRENT_DATE - interval '36' day)
16 GROUP BY 1,2
17 )
18)
19WHERE order_day > (CURRENT_DATE - interval '30' day)
20ORDER BY 1,2
Partition pCLV by brand¶
Use the NTILE() function to partition by brand, and then order by predicted customer lifetime value.
NTILE(100) OVER (PARTITION BY brand ORDER BY predicted_clv desc, _uuid_pk)
Percent rank of purchases¶
Use the PERCENT_RANK() function to return the percent rank of all purchases.
1SELECT DISTINCT
2 amperity_id
3 ,order_revenue
4 ,PERCENT_RANK() OVER (ORDER BY order_revenue) pct_rank
5FROM Unified_Transactions
6WHERE order_datetime > DATE('2021-04-01') AND amperity_id IS NOT NULL
7ORDER BY pct_rank DESC
Purchasers, 12-month rolling window¶
The following query returns a distinct count of purchasers during the previous 12 months.
1WITH periods AS (
2 SELECT DISTINCT
3 YEAR(order_datetime) order_year
4 ,MONTH(order_datetime) order_month
5 FROM Unified_Transactions
6)
7
8,amp_base AS (
9 SELECT
10 amperity_id
11 ,order_year
12 ,order_month
13 FROM (
14 SELECT DISTINCT
15 amperity_id
16 FROM Unified_Transactions
17 ) ut
18 CROSS JOIN periods
19)
20
21,purchase_by_month AS (
22 SELECT
23 YEAR(order_datetime) purchase_year
24 ,MONTH(order_datetime) purchase_month
25 ,amperity_id AS purchase_amperity_id
26 ,COUNT(distinct order_id) orders
27 FROM Unified_Transactions
28 GROUP BY 1,2,3
29)
30
31,all_together AS (
32 SELECT
33 order_year
34 ,order_month
35 ,amperity_id
36 ,SUM(orders) order_month_total
37 ,SUM(SUM(orders)) OVER (
38 PARTITION BY amperity_id
39 ORDER BY order_year, order_month
40 ROWS BETWEEN 11 preceding AND current row
41 ) preceding_12_month_orders
42 FROM amp_base
43 LEFT JOIN purchase_by_month
44 ON
45 amp_base.amperity_id=purchase_by_month.purchase_amperity_id
46 AND amp_base.order_year=purchase_by_month.purchase_year
47 AND amp_base.order_month=purchase_by_month.purchase_month
48 GROUP BY 1,2,3
49)
50
51,last12_month_purchasers AS (
52 SELECT
53 order_year
54 ,order_month
55 ,COUNT(DISTINCT CASE
56 WHEN preceding_12_month_orders > 0
57 THEN amperity_id
58 END) last_12_month_purchasers
59 FROM all_together
60 GROUP BY 1,2
61)
62
63SELECT *
64FROM last12_month_purchasers
65ORDER BY 1 DESC, 2 DESC
Product replenishment by date¶
The following query shows how to return results for product replenishment use cases. The query uses first purchase dates and next purchase dates to build timeframes around which a replenishment campaign can be run.
Important
You must configure this query to match your brand’s product catalog.
The product_description field represents the name of a specific product in your product catalog. Update this field to match the field in your product catalog that defines product descriptions. For example, this may be a field that returns a SKU value. Update the “’<string>’” value to match the specific name of a product.
The product_group field represents a category of products within your product catalog. Update this field to match the field in your product catalog that defines product categories.
Use the “AND product_group <> ‘<group>’” line to specify one (or more) groups. Add a line for each group. Remove the line when product categories should not returned for your product replenishment use case.
Explore the query results and tune the query parameters to align to your brand’s product catalog and replenishment use case.
1WITH product_purchases AS (
2 SELECT
3 amperity_id
4 ,product_description
5 ,order_datetime
6 FROM Unified_Itemized_Transactions
7 WHERE product_description = '<string>'
8 AND item_list_price > 0
9 AND product_group <> '<group>'
10 AND item_revenue > 0
11)
12
13,product_purchase_pairs AS (
14 SELECT
15 t1.amperity_id
16 ,t1.product_description
17 ,t1.order_datetime AS first_purchase_date
18 ,MIN(t2.order_datetime) AS next_purchase_date
19 FROM product_purchases t1
20 JOIN product_purchases t2 ON t1.amperity_id = t2.amperity_id
21 AND t1.product_description = t2.product_description
22 AND t1.order_datetime < t2.order_datetime
23 GROUP BY t1.amperity_id
24 ,t1.product_description
25 ,t1.order_datetime
26)
27
28SELECT *
29FROM (
30 SELECT
31 DATE_DIFF(
32 'day'
33 ,first_purchase_date
34 ,next_purchase_date
35 ) AS days_between_purchases
36 ,COUNT(*) AS number_of_customers
37 FROM product_purchase_pairs
38 GROUP BY DATE_DIFF ('day', first_purchase_date, next_purchase_date)
39 ORDER BY days_between_purchases
40)
41WHERE days_between_purchases > 0
Product replenishment by decile¶
The following query shows how to return results for product replenishment use cases. The query uses first purchase dates and next purchase dates to build timeframes, and then groups customers into deciles.
Important
You must configure this query to match your brand’s product catalog.
The product_description field represents the name of a specific product in your product catalog. Update this field to match the field in your product catalog that defines product descriptions. For example, this may be a field that returns a SKU value. Update the “’<string>’” value to match the specific name of a product.
The product_group field represents a category of products within your product catalog. Update this field to match the field in your product catalog that defines product categories.
Use the “AND product_group <> ‘<group>’” line to specify one (or more) groups. Add a line for each group. Remove the line when product categories should not returned for your product replenishment use case.
Explore the query results and tune the query parameters to align to your brand’s product catalog and replenishment use case.
1WITH product_purchases AS (
2 SELECT
3 amperity_id
4 ,product_description
5 ,order_datetime
6 FROM Unified_Itemized_Transactions
7 WHERE item_list_price > 0
8 AND product_group <> 'SAMPLE'
9 AND item_revenue > 0
10)
11
12,product_purchase_pairs AS (
13 SELECT
14 t1.amperity_id
15 ,t1.product_description
16 ,t1.order_datetime AS first_purchase_date
17 ,MIN(t2.order_datetime) AS next_purchase_date
18 FROM product_purchases t1
19 JOIN product_purchases t2 ON t1.amperity_id = t2.amperity_id
20 AND t1.product_description = t2.product_description
21 AND t1.order_datetime < t2.order_datetime
22 GROUP BY t1.amperity_id
23 ,t1.product_description
24 ,t1.order_datetime
25)
26
27,days_diff AS (
28 SELECT
29 amperity_id
30 ,product_description
31 ,DATE_DIFF(
32 'day'
33 ,first_purchase_date
34 ,next_purchase_date
35 ) AS days_between_purchases
36 FROM product_purchase_pairs
37)
38
39,deciles AS (
40 SELECT
41 amperity_id
42 ,product_description
43 ,days_between_purchases
44 ,NTILE(10) OVER (
45 PARTITION BY product_description
46 ORDER BY days_between_purchases ASC
47 ) AS decile
48 FROM days_diff
49 WHERE days_between_purchases > 0
50)
51
52SELECT *
53FROM (
54 SELECT
55 product_description
56 ,AVG(days_between_purchases) AS average_days_between
57 ,COUNT(amperity_id) AS count_replenishments
58 ,COUNT(DISTINCT amperity_id) AS count_customers
59 ,MAX(CASE
60 WHEN decile = 1 THEN days_between_purchases
61 END) AS "Maximum days (1st decile)"
62 ,MAX(CASE
63 WHEN decile = 2 THEN days_between_purchases
64 END) AS "2nd decile"
65 ,MAX(CASE
66 WHEN decile = 3 THEN days_between_purchases
67 END) AS "3rd decile"
68 ,MAX(CASE
69 WHEN decile = 4 THEN days_between_purchases
70 END) AS "4th decile"
71 ,MAX(CASE
72 WHEN decile = 5 THEN days_between_purchases
73 END) AS "5th decile"
74 ,MAX(CASE
75 WHEN decile = 6 THEN days_between_purchases
76 END) AS "6th decile"
77 ,MAX(CASE
78 WHEN decile = 7 THEN days_between_purchases
79 END) AS "7th decile"
80 ,MAX(CASE
81 WHEN decile = 8 THEN days_between_purchases
82 END) AS "8th decile"
83 ,MAX(CASE
84 WHEN decile = 9 THEN days_between_purchases
85 END) AS "9th decile"
86 ,MAX(CASE
87 WHEN decile = 10 THEN days_between_purchases
88 END) AS "Minimum days (10th decile)"
89 FROM deciles
90 GROUP BY 1
91 ORDER BY 3 DESC
92)
Products by revenue¶
Use the following example to return your top 5 products by revenue.
1SELECT
2 product_id
3 ,SUM(item_revenue) AS total_revenue
4FROM Unified_Itemized_Transactions
5WHERE
6 DATE_TRUNC('month', order_datetime) = DATE_TRUNC('month', CURRENT_DATE)
7GROUP BY product_id
8ORDER BY total_revenue DESC
9LIMIT 5
Use the LIMIT clause to configure the top N products.
Rank by product affinity¶
The following query shows an example of how to rank customers by product affinity:
1WITH rankings AS (
2 SELECT
3 amperity_id
4 ,product_attribute
5 ,RANK() OVER (PARTITION BY amperity_id ORDER BY ranking) rec_ranking
6 FROM Predicted_Affinity_Table
7 WHERE ranking < 10000000
8)
9,top_5_by_customers AS (
10 SELECT
11 amperity_id
12 ,MAX(CASE WHEN rec_ranking=1 THEN product_attribute END) prod_ranking_1
13 ,MAX(CASE WHEN rec_ranking=2 THEN product_attribute END) prod_ranking_2
14 ,MAX(CASE WHEN rec_ranking=3 THEN product_attribute END) prod_ranking_3
15 ,MAX(CASE WHEN rec_ranking=4 THEN product_attribute END) prod_ranking_4
16 ,MAX(CASE WHEN rec_ranking=5 THEN product_attribute END) prod_ranking_5
17 FROM rankings
18 GROUP BY 1
19)
20
21SELECT *
22FROM top_5_by_customers
Use the WHERE clause to set the maximum threshold for product affinity:
WHERE ranking < some_value
For example, if the threshold is “10000000” then a customer is more than ten millionth in product affinity, then that customer will not be included in the ranking.
Rank by Amperity ID¶
Use the RANK() function to find the largest transactions by Amperity ID, and then return them in ascending order:
1WITH ranked_transactions AS (
2 SELECT
3 t.orderid
4 ,t.amperity_id
5 ,t.transactiontotal
6 ,RANK() OVER (
7 PARTITION BY t.amperity_id
8 ORDER BY t.transactiontotal DESC
9 ) AS rank
10 FROM TransactionsEcomm t
11 ORDER BY t.amperity_id, rank ASC
12 LIMIT 100
13)
14SELECT * FROM ranked_transactions WHERE rank = 1
Reachable email, by state¶
The following example shows how to return a list of customers with reachable email addresses, by state:
1SELECT
2 COUNT(amperity_id) AS count
3 ,state
4 ,SUM(email_count) AS has_email
5FROM (
6 SELECT
7 c.amperity_id
8 ,state
9 ,IF(e.amperity_id IS NULL,0,1) AS email_count
10 FROM Customer_360 c
11 LEFT JOIN (
12 SELECT DISTINCT amperity_id FROM Email_Table agg
13 WHERE agg.email_opt_out_field = 'No'
14 AND agg.email_out_status = 'opt-in'
15 ) e ON e.amperity_id = c.amperity_id
16WHERE state IS NOT NULL)
17GROUP BY state
Return percentiles¶
Return 10th percentiles:
1SELECT
2 tier*.1 AS tier
3 ,MIN(lifetime_order_revenue) lifetime_revenue_amount
4FROM (
5 SELECT
6 amperity_id
7 ,lifetime_order_revenue
8 ,NTILE(10) OVER (ORDER BY lifetime_order_revenue) tier
9 FROM Transaction_Attributes_Extended
10)
11GROUP BY 1
12ORDER BY 1
Return percentiles by 4:
1SELECT
2 tier*.25 tier
3 ,MIN(lifetime_order_revenue) lifetime_revenue_amount
4FROM (
5 SELECT DISTINCT
6 amperity_id
7 ,lifetime_order_revenue
8 ,NTILE(4) OVER (ORDER BY lifetime_order_revenue) tier
9 FROM Transaction_Attributes_Extended
10)
11GROUP BY 1
12ORDER BY 1
Revenue by month¶
The following query returns three values for each month in a calendar year: revenue for customers who are in your top 20 percent, total revenue, and the percent of total revenue your top 20 percent customers represent.
1SELECT
2 month
3 ,SUM(CASE
4 WHEN top_20
5 THEN revenue
6 ELSE NULL
7 END) revenue
8 ,SUM(revenue) total_revenue
9 ,1.00*SUM(CASE
10 WHEN top_20
11 THEN revenue
12 ELSE NULL
13 END)/SUM(revenue) percent
14FROM (
15 SELECT
16 month
17 ,amperity_id
18 ,revenue
19 ,CASE
20 WHEN rank IN (9,10) THEN TRUE
21 ELSE FALSE
22 END AS top_20
23 FROM (
24 SELECT
25 MONTH(order_datetime) AS month
26 ,amperity_id
27 ,SUM(order_revenue) revenue
28 ,NTILE(10) OVER (ORDER BY SUM(order_revenue)) rank
29 FROM Unified_Transactions
30 WHERE YEAR(order_datetime)=2022
31 GROUP BY 1,2
32 )
33)
34GROUP BY 1
35ORDER BY month ASC
Revenue opportunity, 1 group¶
Use the following example to size revenue opportunity for a single group, such as for “one-and-done” or for “repeat purchaser”. This query is most effective when run against the Transaction Attributes Extended table, but may be useful when run against other tables. Customize the WHERE clause in the WITH statement to define the group. Customize the main SELECT statement to define the time periods to measure.
1WITH customers AS (
2 SELECT
3 amperity_id
4 FROM Transaction_Attributes_Extended
5 WHERE one_and_done
6)
7
8SELECT
9 COUNT(DISTINCT tae.amperity_id) AS num_customers
10 ,AVG(lifetime_average_order_value) AS lt_aov
11 ,AVG(L12M_average_order_value) AS l12m_aov
12 ,AVG(lifetime_order_revenue) AS lt_rev
13 ,AVG(l12m_order_revenue) AS l12m_rev
14 ,AVG(lifetime_average_item_price) AS lt_aur
15 ,AVG(l12m_average_item_price) AS l12m_aur
16 ,AVG(lifetime_order_frequency) AS avg_lt_orders
17 ,AVG(l12m_order_frequency) AS avg_l12m_orders
18 ,SUM(l12m_order_revenue) AS total_l12m_revenue
19FROM customers cust
20INNER JOIN Transaction_Attributes_Extended tae
21ON cust.amperity_id=tae.amperity_id
Revenue opportunity, many groups¶
Use the following example to size revenue opportunity for multiple groups, such as moving from “one-and-done” to “repeat purchaser”. This query is most effective when run against the Transaction Attributes Extended table, but may be useful when run against other tables. Customize the WITH statement to define a list of fields to be used for comparing movements between groups. Customize the main SELECT statement to define the time periods for which this movement will be measured.
1WITH customers AS (
2 SELECT
3 amperity_id
4 ,multi_purchase_channel
5 FROM Transaction_Attributes_Extended
6)
7
8SELECT
9 cust.multi_purchase_channel
10 ,COUNT(DISTINCT tae.amperity_id) AS num_customers
11 ,AVG(lifetime_average_order_value) AS lt_aov
12 ,AVG(L12M_average_order_value) AS l12m_aov
13 ,AVG(lifetime_order_revenue) AS lt_rev
14 ,AVG(l12m_order_revenue) AS l12m_rev
15 ,AVG(lifetime_average_item_price) AS lt_aur
16 ,AVG(l12m_average_item_price) AS l12m_aur
17 ,AVG(lifetime_order_frequency) AS avg_lt_orders
18 ,AVG(l12m_order_frequency) AS avg_l12m_orders
19 ,SUM(l12m_order_revenue) AS total_l12m_revenue
20FROM customers cust
21INNER_JOIN Transaction_Attributes_Extended tae
22ON cust.amperity_id=tae.amperity_id
23GROUP BY 1
Top 20% revenue during previous year¶
Which customers are your top 20-percent for revenue during the previous year?
Use the monetary component of approximate RFM scores to quickly find customers who are in the top 20-percent, along with the percentage of total revenue those customers represent.
Tip
Divide percentage of revenue by the number of customers in the top 20 to find the average percentage of revenue that is generated by each customer across all of your brands, stores, channels, and regions.
The following example returns your top 20 percent of customers by revenue:
1SELECT
2 amperity_id
3 ,L12M_monetary
4FROM Transaction_Attributes_Extended
5WHERE L12M_monetary >= 9
Totals by brand¶
Use the SUM() function to build totals for quantity, returns, and revenue:
Total quantity by brand¶
Use the SUM() function to return total quantity by brand, and then sort in descending order:
1SELECT
2 purchase_brand
3 ,SUM(order_quantity) AS total_quantity
4FROM Unified_Transactions
5GROUP BY 1
6ORDER BY 1 DESC
Total returns by brand¶
Use the SUM() function to return total returns by brand, and then sort in descending order:
1SELECT
2 purchase_brand
3 ,SUM(order_returned_revenue) AS total_returns
4FROM Unified_Transactions
5GROUP BY 1
6ORDER BY 1 DESC
Total revenue by brand¶
Use the SUM() function to return total revenue by brand, and then sort in descending order:
1SELECT
2 purchase_brand
3 ,SUM(order_revenue) AS total_revenue
4FROM Unified_Transactions
5GROUP BY 1
6ORDER BY 1 DESC
Transactions by day¶
Use the following example to generate a breakdown of customer and order metrics by day. This query must be run against the Unified Transactions table. Export the results of this query to BI tools like Tableau or desktop tools like Google Sheets or Microsoft Excel.
1SELECT
2 DATE(order_datetime) AS day_of_order
3 ,purchase_brand
4 ,purchase_channel
5 ,SUM(order_revenue) AS total_revenue
6 ,AVG(order_revenue) AS aov
7 ,COUNT(DISTINCT order_id) AS orders
8 ,COUNT(DISTINCT amperity_id) AS customers
9 ,SUM(order_quantity) AS total_items
10 ,AVG(order_quantity) AS avg_items
11 ,SUM(order_returned_revenue) AS total_returned_revenue
12FROM Unified_Transactions
13GROUP BY 1,2,3
14ORDER BY 1,2,3