SQL Server:将类似的销售分组
我正在尝试用SQL Server做一些报告。 以下是基本的表格设置: 订单(ID、创建日期、状态) 产品(ID、名称、价格) 订单与产品映射(订单ID、产品ID、数量、价格、订购日期) 在这里,我想创建一份报告,向集团产品报告在一段时间内类似的销售额,如下所示: 超过1个月的销售额: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美元) 请注意,()中的
如何在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);