SQL Server:将类似的销售分组

SQL Server:将类似的销售分组,sql,sql-server,group-by,sql-server-2012,aggregate-functions,Sql,Sql Server,Group By,Sql Server 2012,Aggregate Functions,我正在尝试用SQL Server做一些报告。 以下是基本的表格设置: 订单(ID、创建日期、状态) 产品(ID、名称、价格) 订单与产品映射(订单ID、产品ID、数量、价格、订购日期) 在这里,我想创建一份报告,向集团产品报告在一段时间内类似的销售额,如下所示: 超过1个月的销售额: 可口可乐、百事可乐、老虎:平均20000美元(可口可乐:21000美元,百事可乐:19000美元,老虎:20000美元) 面包、肉类:平均10000美元(面包:11000美元,肉类:9000美元) 请注意,()中的

我正在尝试用SQL Server做一些报告。 以下是基本的表格设置:

订单(ID、创建日期、状态)

产品(ID、名称、价格)

订单与产品映射(订单ID、产品ID、数量、价格、订购日期)

在这里,我想创建一份报告,向集团产品报告在一段时间内类似的销售额,如下所示:

超过1个月的销售额:

  • 可口可乐、百事可乐、老虎:平均20000美元(可口可乐:21000美元,百事可乐:19000美元,老虎:20000美元)
  • 面包、肉类:平均10000美元(面包:11000美元,肉类:9000美元)
  • 请注意,()中的文本只是为了澄清,不需要在报告中说明)。 用户定义销售之间可以考虑类似的变化。销售额低于5%的示例销售被认为是相似的,应该分组在一起。时间段也是用户定义的

    我可以计算一段时间内的总销售额,但不知道如何根据销售额将它们组合在一起。我正在使用SQL Server 2012。 感谢您的帮助

    对不起,我的英语不是很好:)

    更新:*我找到了我真正需要的东西;)*

    对于已知的数字数组,如:1,2,3,50,52100102105

    我需要将他们分成至少有3个数字的组,并且组中任意两个项目之间的差异小于10

    对于上述阵列,输出应为:

    [1,2,3]

    [100102105]

    =>该算法采用3个参数:数组、组成一个组的最小项和两个项之间的最大差值


    如何在C#中实现这一点?

    此查询应产生您所期望的结果,它显示您每个月的订单销售情况:

    SELECT CONVERT(CHAR(4), OP.DateOrdered, 100) + CONVERT(CHAR(4), OP.DateOrdered, 120) As Month , 
    Product.Name , 
    AVG( OP.Quantity * OP.Price ) As Turnover
    FROM Order_Product_Mapping OP
    INNER JOIN Product ON Product.ID = OP.ProductID
    GROUP BY  CONVERT(CHAR(4), OP.DateOrdered, 100) + CONVERT(CHAR(4), OP.DateOrdered, 120) ,
              Product.Name
    

    未测试,但如果您提供示例数据,我可以处理它

    看起来我让事情变得比应该的更复杂。 以下是解决问题的方法:

    -运行查询以获取每个产品的销售额


    -运行K-mean或一些类似的算法

    我简直不敢相信是我干的~~~

    -- this threshold is the key in this query
    -- it means that 
    -- if the difference between two values are less than the threshold
    -- these two values are belong to one group
    -- in your case, I think it is 200
    DECLARE @th int
    SET @th = 200
    
    -- very simple, calculate total price for a time range
    ;WITH totals AS ( 
      SELECT p.name AS col, sum(o.price * op.quantity) AS val
      FROM order_product_mapping op
      JOIN [order] o ON o.id = op.orderid
      JOIN product p ON p.id = op.productid
      WHERE dateordered > '2013-03-01' AND dateordered < '2013-04-01'
      GROUP BY p.name
    ),
    -- give a row number for each row
    cte_rn AS ( -- 
      SELECT col, val, row_number()over(ORDER BY val DESC) rn
      FROM totals
    ),
    -- show starts now,
    -- firstly, we make each row knows the row before it 
    cte_last_rn AS (
      SELECT col, val, CASE WHEN rn = 1 THEN 1 ELSE rn - 1 END lrn
      FROM cte_rn
    ),
    -- then we join current to the row before it, and calculate 
    -- the difference between the total price of current row and that of previous row
    -- if the the difference is more than the threshold we make it '1', otherwise '0'
    cte_range AS (
      SELECT
        c1.col, c1.val,
        CASE
          WHEN c2.val - c1.val <= @th THEN 0
          ELSE 1
        END AS range,
        rn
      FROM cte_last_rn c1
      JOIN cte_rn c2 ON lrn = rn
    ),
    -- even tricker here,
    -- now, we join last cte to itself, and for each row
    -- sum all the values (0, 1 that calculated previously) of rows before current row
    cte_rank AS (
      SELECT c1.col, c1.val, sum(c2.range) rank
      FROM cte_range c1
      JOIN cte_range c2 ON c1.rn >= c2.rn
      GROUP BY c1.col, c1.val
    )
    -- now we have properly grouped theres total prices, and we can group on it's rank 
    SELECT 
      avg(c1.val) AVG,
      (
        SELECT c2.col + ', ' AS 'data()'
        FROM cte_rank c2
        WHERE c2.rank = c1.rank
        ORDER BY c2.val desc
        FOR xml path('')
      ) product,
      (
        SELECT cast(c2.val AS nvarchar(MAX)) + ', ' AS 'data()'
        FROM cte_rank c2
        WHERE c2.rank = c1.rank
        ORDER BY c2.desc
        FOR xml path('')
      ) price
    FROM cte_rank c1
    GROUP BY c1.rank
    HAVING count(1) > 2
    
    要了解我是如何连接的,请阅读以下内容:

    顺便说一句,如果你只是想要c#:

    var最大差值=10;
    var minItems=3;
    //我只是假设您的列表没有排序,所以请先排序
    var数组=(新列表{3,2,50,1,51100105102});
    var result=新列表();
    var group=新列表();
    var lastNum=array.First();
    var totalDiff=0;
    foreach(数组中的变量n)
    {
    totalDiff+=n-lastNum;
    //如果当前编号与当前组中第一个编号的距离
    //小于阈值,请添加到当前组
    if(totalDiff=minItems)
    结果:添加(组);
    //创建新组
    组=新列表(){n};
    lastNum=n;
    totalDiff=0;
    }
    //忘记了最后一组。。。
    如果(group.Count>=最小项)
    结果:添加(组);
    

    这里的关键是,需要对数组进行排序,这样就不需要跳转或存储值来计算距离

    这就是我在一段时间内计算每个产品的距离所做的。我真正想要的是一起销售类似产品的集团产品。例如,可口可乐、百事可乐和老虎都有类似的销售。我想到的一种方法是将销售范围细分为更小的范围。例如,如果古柯是销量100万美元的最畅销产品,那么我们将其细分为更小的范围:0-100k、100k-200k、900k-1M。然后我们可以找到每个范围内总销售额的平均值,然后加上和减去用户定义的百分比以缩小结果范围。但我认为应该有更好的方法。我认为这是某种聚类分析?有点小问题。假设你的销售额为100、104、108、112等等,最多200辆——所有这些都比前一辆多2%-4%。在这种情况下,你会如何分组销售?嗨,你能看看我的答案,并告诉我它是否有效吗?非常确定~~这是可行的,kk~~同意这太复杂了,但在很多情况下,如果你能在sql中使用它,它会比在codeNo中使用更快,因为你所做的只是检查两个值之间的差异。组中的所有项目的差异不应大于阈值。例如,如果阈值为3,则1,2,3在组中,但1,2,5不在组中,因为5-1=4>3。我仍在寻找解决方案。对于1,2,5,可能阈值应该是4?或者1,2应该在一个组中,5应该在另一个组中?是否只是选择合适的阈值的问题?你如何确定一个组中应该有什么?我只是认为这里的算法是计算相邻的两个值之间的距离(因为这些值已经被排序),kkk,也许我对此太兴奋了..是的,哈哈,我不知道xml路径('')如果不是因为这个问题,我很难放弃SQL。我刚看到你最新的问题。我认为我的SQL可以生成您想要的输出。只需在查询的末尾添加'having count(1)>2'。对于1,2,3,50,51100102105的数组,如果将阈值更改为3,它将为您提供两个组:1,2,3和100102105。检查我的更新答案此代码中有一些错误。我会在完成修复后发布:DMy bad。。我家里没有IDE,所以无法尝试,很抱歉有bug。我发现我忘记了最后一组。已经修复了ithaha,天才开发者:)我正忙着帮我的女孩写报告。非常感谢:)这适用于2个数字之间的阈值。要让它检查整个组,我们应该在当前数字和组中的第一个数字之间添加一个测试Hey。。。我是个白痴。。。我想念你的问题。。。你是说组中任意两个数字之间的最大距离。。。难怪SQL不起作用。。顺便说一下,我已经修复了我的c代码。稍后我将尝试SQL。
    AVG     PRODUCT     PRICE
    28      A, B, C     30, 29, 27
    12      D, E, F     15, 12, 10
    3       G, H, I     4, 3, 2
    
    var maxDifference = 10;
    var minItems = 3;     
    
    // I just assume your list is not ordered, so order it first
    var array = (new List<int> {3, 2, 50, 1, 51, 100, 105, 102}).OrderBy(a => a);
    
    var result = new List<List<int>>();
    var group = new List<int>();
    var lastNum = array.First();
    var totalDiff = 0;
    foreach (var n in array)
    {
        totalDiff += n - lastNum;
    
        // if distance of current number and first number in current group
        // is less than the threshold, add into current group
        if (totalDiff <= maxDifference)
        {
            group.Add(n); 
            lastNum = n;
            continue;
        }
    
        // if current group has 3 items or more, add to final result
        if (group.Count >= minItems)
            result.Add(group);
    
        // start new group
        group = new List<int>() { n };
        lastNum = n;
        totalDiff = 0;   
    }
    
    // forgot the last group...
    if (group.Count >= minItems)
        Result.Add(group);