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.

Use the Queries page.

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.

Use the SQL Segment Editor on the Queries page.

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

  1. From the Queries page click Create, and then select SQL Query. This opens the SQL Query Editor.

  2. Under Database, select a database. The Customer 360 database is selected by default.

  3. Define the query against the selected database.

  4. Click Run Query and debug any issues that may arise.

  5. 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

  1. From the Queries page, open the menu for a query, and then select View. This opens a query editor.

  2. Click Run Query to run the query. Wait for it to return results.

  3. Example the columns and the data that is in them.

  4. Adjust your query as necessary until it runs correctly.

  5. 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

  1. 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.

  2. Under Being Sent To click Add one now. This opens the Add Orchestration dialog box.

  3. Follow the steps to add an orchestration. The steps will vary depending on the destination, the data template, and the orchestration.

  4. 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