提问人:TFabris 提问时间:9/13/2023 最后编辑:marc_sTFabris 更新时间:9/14/2023 访问量:70
通过将 XML 列中的 UnitPrice 和 Quantity 相乘来对 TotalPrice 进行 SQL 查询
SQL query for TotalPrice by multiplying UnitPrice and Quantity which are inside a XML column
问:
我需要查询 SQL Server 数据库表,但我无法控制此数据库的设计有多糟糕。我想要从如下所示的数据中获得截至今天的最后 X 天的实际每日总资金(日期以 UTC 存储):
SQL Server 表包含如下行:
Customer Customer1
CreateDate 2023-09-11 8:00 am
Items (column contains XML)
<ArrayOfItems>
<Item>
<Quantity>5</Quantity>
<UnitPrice>3.00</UnitPrice>
</Item>
</ArrayOfItems>
Customer Customer2
CreateDate 2023-09-11 10:00 am
Items (column contains XML)
<ArrayOfItems>
<Item>
<Quantity>1</Quantity>
<UnitPrice>5.50</UnitPrice>
</Item>
<Item>
<Quantity>4</Quantity>
<UnitPrice>4.00</UnitPrice>
</Item>
</ArrayOfItems>
Customer Customer3
CreateDate 2023-09-12 4:00 pm
Items (column contains XML)
<ArrayOfItems>
<Item>
<Quantity>2</Quantity>
<UnitPrice>5.00</UnitPrice>
</Item>
<Item>
<Quantity>3</Quantity>
<UnitPrice>3.50</UnitPrice>
</Item>
</ArrayOfItems>
我希望结果是每天的总数量和价格。例如,我希望输出为:
DateCreated TotalQuantity TotalPrice
-----------------------------------------
2023-09-11 10 36.50
2023-09-12 5 20.50
显然,如果在数据输入时计算总数量并放入自己的列中,那会更好,这样我就可以用总和进行简单的查询。唉,它不是那样设计的,我被我所拥有的东西困住了。UnitPrice
这是我到目前为止所做的工作(注意:在此查询中,我在 DateADD 部分输入了“2”的年龄,该部分获取最近两天的数据。我可以将其更改为过去的任意天数,例如,我可以通过在其中输入“30”来获得过去 30 天,或者我可以使用其他日期算法。日期不是我需要帮助的,而是我需要帮助的 TotalPrice):
SELECT
CONVERT(date, CreateDate) AS DateCreated,
SUM (Items.value('sum(/ArrayOfItems/Item/Quantity)', 'int') ) AS TotalQuantity
FROM
Invoices
WHERE
DATEADD(day, 2, CreateDate) > GetUtcDate()
GROUP BY
CONVERT(date, CreateDate)
上面的 SQL 查询只让我走了一半。它获取前两列,但不获取最后一列。我不知道如何让它乘以每个项目的时间,然后将每天的所有时间相加。它必须一行一行地做,因为每一行都是不同的。到目前为止,我有这个,它让我得到了数量,但不是当天的总价。我如何获得它?Quantity
UnitPrice
UnitPrice
我尝试过搜索,但没有一个搜索结果给我这个确切的场景。我敢肯定以前一定有人问过这个问题,但我找不到它,至少,不是这个确切的场景。我没有看到任何东西告诉我如何像这样迭代每天的行。
DateCreated TotalQuantity
---------------------------
2023-09-11 10
2023-09-12 5
答:
在转换为正确的类型后,可以使用 XQuery 的 FLWOR 表达式循环访问和乘法值
Select
Convert(date,CreateDate) as DateCreated,
SUM ( Items.value('
sum(
for $i in /ArrayOfItems/Item
return xs:int(($i/Quantity/text())[1]) * xs:decimal(($i/UnitPrice/text())[1])
)', 'decimal(18,2)') ) AS TotalQuantity
from
Invoices
where
CreateDate > DateADD(day, -2, GETUTCDATE())
group by
Convert(date, CreateDate)
请注意对子句的更改,以启用要使用的索引。WHERE
另一种方法是切碎节点,并分别对它们求和Item
Select
Convert(date,CreateDate) as DateCreated,
SUM (
x.Item.value('(Quantity/text())[1]','int') *
x.Item.value('(UnitPrice/text())[1]', 'decimal(18,2)')
) AS TotalQuantity
from
Invoices
CROSS APPLY Items.nodes('/ArrayOfItems/Item') x(Item)
where
CreateDate > DateADD(day, -2, GETUTCDATE())
group by
Convert(date, CreateDate)
评论
decimal
decimal(38,19)
ORDER BY
请尝试以下解决方案。
它使用 XQuery 方法并粉碎 XML。.nodes()
.value()
CTE 后的数据以矩形/关系格式出现。剩下的就是基本分组和所需列的 SUM()。
SQL格式
-- DDL and sample data population, start
DECLARE @tbl TABLE (ID INT IDENTITY PRIMARY KEY, CreateDate DATE, Items XML);
INSERT @tbl (CreateDate, Items) VALUES
('2023-09-11', N'<ArrayOfItems>
<Item>
<Quantity>5</Quantity>
<UnitPrice>3.00</UnitPrice>
</Item>
</ArrayOfItems>'),
('2023-09-11', N'<ArrayOfItems>
<Item>
<Quantity>1</Quantity>
<UnitPrice>5.50</UnitPrice>
</Item>
<Item>
<Quantity>4</Quantity>
<UnitPrice>4.00</UnitPrice>
</Item>
</ArrayOfItems>'),
('2023-09-12', N'<ArrayOfItems>
<Item>
<Quantity>2</Quantity>
<UnitPrice>5.00</UnitPrice>
</Item>
<Item>
<Quantity>3</Quantity>
<UnitPrice>3.50</UnitPrice>
</Item>
</ArrayOfItems>');
-- DDL and sample data population, end
WITH rs AS
(
SELECT t.ID, t.CreateDate
, c.value('(Quantity/text())[1]', 'INT') AS Quantity
, c.value('(UnitPrice/text())[1]', 'MONEY') AS UnitPrice
, c.value('(Quantity/text())[1]', 'INT') * c.value('(UnitPrice/text())[1]', 'MONEY') AS TotalPrice
FROM @tbl AS t
CROSS APPLY Items.nodes('/ArrayOfItems/Item') AS t1(c)
)
-- SELECT * FROM rs -- uncomment this line to see the data
SELECT CreateDate
, SUM(Quantity) AS TotalQuantity
, SUM(TotalPrice) AS TotalPrice
FROM rs
GROUP BY rs.CreateDate;
输出
创建日期 | 总数量 | 总价 |
---|---|---|
2023-09-11 | 10 | 36.50 |
2023-09-12 | 5 | 20.50 |
评论
TotalPrice
Quantity
UnitPrice
SUM(Quantity * UnitPrice)
money
decimal
.value()
SELECT * FROM rs
评论