是否有任何指导方针或经验法则来确定何时存储聚合值以及何时动态计算它们?
例如,假设我有用户可以评分的小部件(请参阅下面的架构)。每次我显示一个小部件时,我都可以从Ratings表格中计算出平均用户评分。或者,我可以将平均评分存储在Widget表格中。这将使我不必在每次显示小部件时计算评分,但是每次用户对小部件进行评分时我都必须重新计算平均评分。
Ratings Widgets
--------- -------
widget_id widget_id
user_id name
rating avg_rating <--- The column in question
Run Code Online (Sandbox Code Playgroud) 我正在为一个简单的银行数据库编写架构。以下是基本规格:
银行应用程序将专门通过存储过程与其数据库通信。
我希望这个数据库每天接受数十万个新事务,以及更高数量级的平衡查询。为了非常快速地提供余额,我需要预先汇总它们。同时,我需要保证余额永远不会与其交易历史相矛盾。
我的选择是:
有一个单独的balances表并执行以下操作之一:
将事务应用于transactions和balances表。TRANSACTION在我的存储过程层使用逻辑来确保余额和交易始终同步。(由杰克支持。)
将交易应用到transactions表并有一个触发器,balances用交易金额为我更新表。
将交易应用到balances表中,并有一个触发器transactions为我在表中添加一个新条目,其中包含交易金额。
我必须依靠基于安全的方法来确保在存储过程之外不能进行任何更改。否则,例如,某些进程可以直接将事务插入transactions表中,并且在该方案1.3下相关余额将不同步。
有一个balances索引视图,可以适当地聚合事务。存储引擎保证余额与其交易保持同步,因此我不需要依赖基于安全的方法来保证这一点。另一方面,我不能再强制余额为非负数,因为视图——甚至索引视图——不能有CHECK约束。(由丹尼支持。)
只有一个transactions表,但有一个额外的列来存储该交易执行后立即生效的余额。因此,用户和货币的最新交易记录也包含其当前余额。(下面由Andrew建议;由garik提出的变体。)
当我第一次解决这个问题时,我阅读了这 两个讨论并决定了 option 2。作为参考,您可以在此处查看它的基本实现。
您是否设计或管理过这样的具有高负载配置文件的数据库?你对这个问题的解决方案是什么?
你认为我做出了正确的设计选择吗?有什么我应该记住的吗?
例如,我知道对transactions表的架构更改需要我重建balances视图。即使我正在归档事务以保持数据库较小(例如,将它们移到其他地方并用摘要事务替换它们),每次架构更新时都必须重建数千万个事务的视图,这可能意味着每次部署的停机时间会显着增加。
如果索引视图是要走的路,我如何保证没有余额为负?
归档交易:
让我详细说明一下存档交易和我上面提到的“摘要交易”。首先,在像这样的高负载系统中,定期存档将是必要的。我想保持余额与其交易历史之间的一致性,同时允许将旧交易转移到其他地方。为此,我将用每个用户和货币的金额摘要替换每批存档交易。
因此,例如,此交易列表:
user_id currency_id amount is_summary
------------------------------------------------
3 1 10.60 …Run Code Online (Sandbox Code Playgroud) sql-server-2008 database-design sql-server aggregate materialized-view
我似乎记得(在 Oracle 上) utteringselect count(*) from any_table和select count(any_non_null_column) from any_table.
如果有的话,这两个陈述之间有什么区别?
在 Oracle 11.2 之前,我使用自定义聚合函数将一列连接成一行。11.2 增加了这个LISTAGG功能,所以我想改用它。我的问题是我需要消除结果中的重复项,但似乎无法做到这一点。
这是一个例子。
CREATE TABLE ListAggTest AS (
SELECT rownum Num1, DECODE(rownum,1,'2',to_char(rownum)) Num2 FROM dual
CONNECT BY rownum<=6
);
SELECT * FROM ListAggTest;
Run Code Online (Sandbox Code Playgroud)
CREATE TABLE ListAggTest AS (
SELECT rownum Num1, DECODE(rownum,1,'2',to_char(rownum)) Num2 FROM dual
CONNECT BY rownum<=6
);
SELECT * FROM ListAggTest;
Run Code Online (Sandbox Code Playgroud)
我想看到的是:
NUM1 NUM2
---------- ---------------------
1 2
2 2 << Duplicate 2
3 3
4 4
5 5
6 6
Run Code Online (Sandbox Code Playgroud)
这是一个listagg接近但不消除重复的版本。
SELECT Num1, listagg(Num2,'-') WITHIN GROUP (ORDER BY NULL) OVER …Run Code Online (Sandbox Code Playgroud) 如果没有返回行,我想为应该使用的列使用默认值。这在 PostgreSQL 中可能吗?我该怎么做?或者我还有其他方法可以解决这个问题吗?
例如这样的事情:
SELECT MAX(post_id) AS max_id DEFAULT 0 FROM my_table WHERE org_id = 3
Run Code Online (Sandbox Code Playgroud)
如果org_id = 3表中没有行,我想返回0。
的ANSI SQL标准定义(第6.5章,集功能规范),用于空的结果集的集合函数以下行为:
COUNT(...) = 0
AVG(...) = NULL
MIN(...) = NULL
MAX(...) = NULL
SUM(...) = NULL
Run Code Online (Sandbox Code Playgroud)
为 AVG、MIN 和 MAX 返回 NULL 非常有意义,因为空集的平均值、最小值和最大值是未定义的。
然而,最后一个让我感到困扰:在数学上,空集的 SUM 是明确定义的:0。使用 0,加法的中性元素,作为基本情况使一切保持一致:
SUM({}) = 0 = 0
SUM({5}) = 5 = 0 + 5
SUM({5, 3}) = 8 = 0 + 5 + 3
SUM({5, NULL}) = NULL = 0 + 5 + NULL
Run Code Online (Sandbox Code Playgroud)
定义SUM({})为null基本上使“无行”成为不适合其他行的特殊情况:
SUM({}) = NULL = NULL
SUM({5}) = 5 != …Run Code Online (Sandbox Code Playgroud) 为了HAVING在 SQL 查询中使用,必须有一个GROUP BY聚合列名吗?
是否有任何特殊情况可以在HAVING没有GROUP BYSQL 查询的情况下使用?
它们必须同时共存吗?
我想从 6 列中获取最小值。
到目前为止,我已经找到了三种方法来实现这一点,但我担心这些方法的性能,并想知道哪种方法对性能更好。
第一种方法是使用大 case 语句。这是一个包含 3 列的示例,基于上面链接中的示例。我的案例陈述会更长,因为我将查看 6 列。
Select Id,
Case When Col1 <= Col2 And Col1 <= Col3 Then Col1
When Col2 <= Col3 Then Col2
Else Col3
End As TheMin
From MyTable
Run Code Online (Sandbox Code Playgroud)
第二种选择是将UNION运算符与多个选择语句一起使用。我会把它放在一个接受 Id 参数的 UDF 中。
select Id, dbo.GetMinimumFromMyTable(Id)
from MyTable
Run Code Online (Sandbox Code Playgroud)
和
select min(col)
from
(
select col1 [col] from MyTable where Id = @id
union all
select col2 from MyTable where Id = @id
union all
select col3 …Run Code Online (Sandbox Code Playgroud) 我正在尝试组合多个日期范围(我的负载大约为最多 500 个,大多数情况下为 10 个),这些日期范围可能会或可能不会重叠到最大的可能连续日期范围中。例如:
数据:
CREATE TABLE test (
id SERIAL PRIMARY KEY NOT NULL,
range DATERANGE
);
INSERT INTO test (range) VALUES
(DATERANGE('2015-01-01', '2015-01-05')),
(DATERANGE('2015-01-01', '2015-01-03')),
(DATERANGE('2015-01-03', '2015-01-06')),
(DATERANGE('2015-01-07', '2015-01-09')),
(DATERANGE('2015-01-08', '2015-01-09')),
(DATERANGE('2015-01-12', NULL)),
(DATERANGE('2015-01-10', '2015-01-12')),
(DATERANGE('2015-01-10', '2015-01-12'));
Run Code Online (Sandbox Code Playgroud)
表看起来像:
id | range
----+-------------------------
1 | [2015-01-01,2015-01-05)
2 | [2015-01-01,2015-01-03)
3 | [2015-01-03,2015-01-06)
4 | [2015-01-07,2015-01-09)
5 | [2015-01-08,2015-01-09)
6 | [2015-01-12,)
7 | [2015-01-10,2015-01-12)
8 | [2015-01-10,2015-01-12)
(8 rows)
Run Code Online (Sandbox Code Playgroud)
预期结果:
combined
--------------------------
[2015-01-01, 2015-01-06)
[2015-01-07, 2015-01-09)
[2015-01-10, ) …Run Code Online (Sandbox Code Playgroud) 当我运行此命令时 SUM()
SELECT COUNT(*) AS [Records], SUM(t.Amount) AS [Total]
FROM dbo.t1 AS t
WHERE t.Id > 0
AND t.Id < 101;
Run Code Online (Sandbox Code Playgroud)
我越来越,
Arithmetic overflow error converting expression to data type int.
Run Code Online (Sandbox Code Playgroud)
关于它的原因是什么的任何想法?
我只是按照这个答案中的说明进行操作。
aggregate ×10
sql-server ×3
null ×2
oracle ×2
postgresql ×2
count ×1
datatypes ×1
mysql ×1
performance ×1
range-types ×1
select ×1
sql-standard ×1