尝试获取客户信息的 SQL 问题

时间:2021-04-08 04:44:49

标签: sql

enter image description here

问题:列出在 2021 年 2 月 15 日这一周内拥有 4 或 5 项活动的用户的所有付费客户;还包括发送的活动中有多少是付费、自然和/或应用程序商店。 (即,为三种来源类型中的每一种都包括一列)。

我目前的尝试:

SELECT source_type, COUNT(*) 
FROM activities 
WHERE activity_time BETWEEN '02-15-21' AND '02-19-21'
GROUP BY source_type

我想就此获得第二意见。我没有包含accounts表,因为我认为这个查询不需要它,但我可能是错的。

2 个答案:

答案 0 :(得分:1)

你试过运行这个吗?它不满足关于四个计数的简介:

  1. 列出所有...客户(符合条件)
    结果中根本没有客户信息,所以这是彻底的失败。

  2. 付费客户 这是顶级标准,只有非免费的客户应包含在结果中。

  3. 标准:有 4 或 5 项活动的用户
    没有尝试在查询中评估此用户条件,结果没有提供足够的信息来推断它。

    • 此要求还存在更多歧义,这是否意味着仅当帐户的个人用户拥有 4 或 5 个活动时才应包含结果,或者该帐户应仅包含 4 或 5 个活动。
    • 立>
    • 如果这是一个测试问题(显然这是人为设计的,如果不是,请寻求有关如何设计更好架构的帮助),那么术语 User 的使用通常非常具体,并表明您需要分组或以其他方式在您的查询中具体使用此方面。
  4. 奖励:(即为三种来源类型中的每一种都包括一列)。
    这是唯一尝试的元素,因为数据按 source_type 分组,但信息无法关联回任何特定用户或客户。

下次请在您的帖子中包含示例数据和预期结果。在为这篇博文准备数据时,您可能会自己遇到这些问题,并且可能会受到启发而提出不同的问题,或者在撰写博文的过程中,您可能已经自己解决了问题。


无需进一步说明,我们仍然可以开始改进此查询,一个好的起点是排除条件并专注于输出的格式。该要求提到了以下输出要求:

  1. 列表Customers
  2. 为每种来源类型包括一列。
<块引用>

首先,即使您认为不需要,请求也明确指出 Customer 是输出中的一个重要方面,并且在您的架构中 account 包含客户信息,因此尽管我们不需要,如果我们确实包含来自 account 表的信息,它会使数据对人类可读。

这是一个标准的 PIVOT 样式响应,然后我们希望每个客户都有一行,显示聚合 source_type 的每个值的计数。大多数 RDBMS 将支持 PIVOT 运算符或函数的某些变体,但是我们可以使用简单的 CASE 表达式实现相同的目的,以有条件地将值放入与我们想要的值匹配的结果集中的投影列中聚合,然后我们可以使用 GROUP BY 来评估聚合,在本例中为 COUNT

<块引用>

以下语法适用于 MS SQL,但是您可以在其他 RBDMS 中轻松实现类似的功能

<块引用>

OP 请用您首选的数据库引擎标记此问题...

注意:在此查询中没有过滤......但

SELECT accounts.company_id
       , accounts.company_name
       , paid = COUNT(st_paid)
       , organic = COUNT(st_organic)
       , app_store = COUNT(st_app_store)
FROM activities 
INNER JOIN accounts ON activities.company_id = accounts.company_id
-- PIVOT the source_type
CROSS APPLY (SELECT st_paid = CASE source_type WHEN 'paid' THEN 1 END
                    ,st_organic = CASE source_type WHEN 'organic' THEN 1 END
                    ,st_app_store = CASE source_type WHEN 'app store' THEN 1 END
             ) as PVT
GROUP BY accounts.company_id, accounts.company_name

这会产生以下结果形状:

<头>
company_id 公司名称 付费 有机 app_store
apl01 苹果 4 8 0
ora01 橙子 6 12 0

标准

当您对结果的 shpe 感到满意并且所有相关信息都可用时,就可以应用标准来过滤这些数据了。

从需求中,可以确定以下标准:

  1. 付款 customers
    该规范没有特别提到付费,但确实包含了一个注释,即(免费客户有 current_mrr = 0

    • 现在我们是否很高兴我们确实加入了帐户表:)
  2. 有 4 或 5 个活动的用户
    这对于明确的 45 活动非常具体,不多也不少。

    • 为了简单起见,我们假设此要求的用户方面并不重要,它只是对帐户中所有用户的引用,而不是只是自己单独记录了 4 或 5 个活动的用户 - 这将需要比我现在想要制造的更多演示数据来证明。
  3. 在 2021 年 2 月 15 日这一周。

    • 在原始帖子中正确识别了这一点,但我们需要将其标为相同的名称。
    <块引用>

    OP 使用了那个星期的星期一到星期五,没有提到星期从 星期一 开始或它们在 星期五 结束,但我们会继续,这只是我们今天需要探索的语法。

现实世界中,标准中指定的实际值应该是参数化的,主要是因为您不想每次都手动重新构建整个查询,而且还要清理输入并防止SQL注入攻击。

<块引用>

尽管对于这篇文章来说似乎有点过分,但即使在简单的查询中使用参数也有助于识别变量元素,因此我将使用第二个条件的参数来演示这个概念。

DECLARE @from DateTime = '2021-02-15' -- Date in ISO format
DECLARE @to DateTime = (SELECT DateAdd(d, 5, @from)) -- will match Friday: 2021-02-19
/* NOTE: requirement only mentioned the start date, not the end
         so your code should also only rely on the single fixed start date */

SELECT accounts.company_id, accounts.company_name
       , paid = COUNT(st_paid), organic = COUNT(st_organic), app_store = COUNT(st_app_store)
FROM activities 
INNER JOIN accounts ON activities.company_id = accounts.company_id
-- PIVOT the source_type
CROSS APPLY (SELECT st_paid = CASE source_type WHEN 'paid' THEN 1 END
                    ,st_organic = CASE source_type WHEN 'organic' THEN 1 END
                    ,st_app_store = CASE source_type WHEN 'app store' THEN 1 END
             ) as PVT
WHERE -- paid accounts = exclude 'free' accounts
      accounts.current_mrr > 0 
      -- Date range filter
      AND activity_time BETWEEN @from AND @to

GROUP BY accounts.company_id, accounts.company_name

-- The fun bit, we use HAVING to apply a filter AFTER the grouping is evaluated
-- Wording was explicitly 4 OR 5, not BETWEEN so we use IN for that
HAVING COUNT(source_type) IN (4,5)

答案 1 :(得分:0)

我相信您在那里遗漏了一些信息。 如果没有关于表的更多信息,我只能猜测您也有一张客户表。我将假设有一个 customer_id 键作为两个表之间的键

我会接受您的查询并执行以下操作:

选择客户 ID, COUNT() AS 总计, MAX(CASE WHEN source_type = "app" THEN "numoperations" END) "app_totals"), MAX(CASE WHEN source_type = "paid" THEN "numoperations" END) "paid_totals"), MAX(CASE WHEN source_type = "organic" THEN "numoperations" END) "organic_totals"), 从 ( SELECT source_type, COUNT() AS num_operations FROM 活动 WHERE activity_time BETWEEN '02-15-21' AND '02-19-21' GROUP BY source_type ) tb1 GROUP BY customer_id

这是我能想到的最通用的情况,但不能很好地扩展。如果获得新的源类型,则需要修改查询,输出表的结构也会发生变化。根据您使用的 sql 引擎(即 mysql 与 microsoft sql),您还可以使用数据透视功能。

前面的查询有点粗糙,但它会给你一个大致的概念。您可以在子句中添加“ELSE”语句,在没有值时将字段归零,如果您只需要活动客户等,则与客户表连接。