在服务器端使用 table 计算 table 列

Calculate table column using over table on server side

假设,db:

中有两个table

Table registries:

       Column       |            Type             |                           
--------------------+-----------------------------+---------
 registry_id        | integer                     | not null 
 name               | character varying           | not null
 ...
 uploaded_at        | timestamp without time zone | not null

Table rows:

    Column     |            Type             | Modifiers 
---------------+-----------------------------+-----------
 row_id        | character varying           | not null
 registry_id   | integer                     | not null
 row           | character varying           | not null

在现实世界中,registries 只是一个 csv 文件,rows 是文件的行。在我的 scala-slick 应用程序中,我想知道每个文件中有多少行。

registries:

1,foo,...
2,bar,...
3,baz,...

rows:

aaa,1,...
bbb,1,...
ccc,2,...

想要的结果:

1,foo,... - 2
2,bar,... - 1
3,baz,... - 0

我现在的代码是 (slick-3.0):

def getRegistryWithLength(rId: Int) = {
    val q1 = registries.filter(_.registryId===rId).take(1).result.headOption
    val q2 = rows.filter(_.registryId===rId).length.result
    val registry = Await.result(db.run(q1), 5.seconds)
    val length = Await.result(db.run(q2), 5.seconds)
    (registry, length)
}

Await 是个坏主意,我知道)

我怎样才能 getRegistryWithLength 使用单个 sql 查询?

我可以将列 row_n 添加到 table registries 中,但是我将被迫在 delete/insert 查询之后更新列 row_n rows table.

如何在数据库服务器端自动计算 table registries 中的 row_n 列?

基本查询可以是:

SELECT r.*, COALESCE(n.ct, 0) AS ct
FROM   registry r
LEFT   JOIN (
   SELECT registry_id, count(*) AS ct
   FROM   rows
   GROUP  BY registry_id
   ) n USING (registry_id);

LEFT [OUTER] JOIN 是必不可少的,因此您不会从 rows.[=22= 中没有相关行的注册表中过滤行]

COALESCE 到 return 0 而不是找不到相关行的 NULL。

SO上有很多相关的回答。这里有一个:

为方便起见,您可以将其包装在 VIEW 中:

CREATE VIEW reg_rn AS
SELECT ...

... 您可以像 table.

一样查询

旁白:使用 reserved SQL key words 作为标识符是不明智的。 row 是列名的禁忌(即使在 Postgres 中允许)。

感谢 Erwin Brandstetter 的精彩回答,使用它,我为我的 scala-slick 应用程序编写了代码。

Scala 代码看起来比普通代码复杂得多 sql:

val registryQuery = registries.filter(_.userId === userId)
val rowQuery = rows groupBy(_.registryId) map { case (regId, rowItems) => (regId, rowItems.length)}
val q = registryQuery joinLeft rowQuery on (_.registryId === _._1) map {
  case (registry, rowsCnt) => (registry, rowsCnt.map(_._2))
}

但它有效!