Knex.js INNER JOIN 结果的 DISTINCT
Posted
技术标签:
【中文标题】Knex.js INNER JOIN 结果的 DISTINCT【英文标题】:DISTINCT on results of a knex.js INNER JOIN 【发布时间】:2020-01-22 18:57:05 【问题描述】:我有两张桌子,metadata
和 view_events
。 metadata
和 view_events
都有 config_id
和 config_type
列。我正在尝试为给定的用户电子邮件选择所有view_events
,由config_id
和config_type
区分,由timestamp, desc
排序,并且仅限于最近的10 个。以下 knex.js 代码不起作用,但希望能表达我想要实现的目标:
return dbClient<AuthenticatedUserIndexRow>(METADATA_TABLE_NAME)
.select([
`$METADATA_TABLE_NAME.$METADATA_COLUMNS.CONFIG_ID`,
`$METADATA_TABLE_NAME.$METADATA_COLUMNS.CONFIG_TYPE`,
`$METADATA_TABLE_NAME.$METADATA_COLUMNS.DESCRIPTION`,
`$VIEW_EVENTS_TABLE_NAME.$VIEW_EVENTS_COLUMNS.TIMESTAMP`,
])
.innerJoin<AuthenticatedUserIndexRow>(VIEW_EVENTS_TABLE_NAME, function innerJoinOnViewEvents()
this.on(
`$METADATA_TABLE_NAME.$METADATA_COLUMNS.STORAGE_ID`,
'=',
`$VIEW_EVENTS_TABLE_NAME.$VIEW_EVENTS_COLUMNS.CONFIG_STORAGE_ID`,
)
.andOn(
`$VIEW_EVENTS_TABLE_NAME.$VIEW_EVENTS_COLUMNS.USER_EMAIL`,
'=',
rawSql('?', [authUserEmail]),
)
.andOn(`$METADATA_TABLE_NAME.$METADATA_COLUMNS.DELETED`, '=', rawSql('?', [false]));
)
.distinct([
`$METADATA_TABLE_NAME.$METADATA_COLUMNS.CONFIG_TYPE`,
`$METADATA_TABLE_NAME.$METADATA_COLUMNS.CONFIG_ID`,
])
.limit(EVENT_LIMIT)
.orderBy(VIEW_EVENTS_COLUMNS.TIMESTAMP, 'desc');
例如,给定以下表格:
view_events
+-------------+-----------+--------------------------+----------------------+
| config_type | config_id | timestamp | email |
+-------------+-----------+--------------------------+----------------------+
| a | foo | 2020-01-23T03:08:14.618Z | john.smith@gmail.com |
| a | foo | 2020-01-23T03:08:14.500Z | jane.doe@gmail.com |
| a | foo | 2020-01-23T03:08:13.618Z | john.smith@gmail.com |
| a | bar | 2020-01-23T03:08:12.618Z | john.smith@gmail.com |
| a | foo | 2020-01-23T03:08:11.618Z | john.smith@gmail.com |
| b | foo | 2020-01-23T03:08:10.618Z | john.smith@gmail.com |
| a | baz | 2020-01-23T03:08:09.618Z | john.smith@gmail.com |
| a | foo | 2020-01-23T03:08:08.618Z | john.smith@gmail.com |
+-------------+-----------+--------------------------+----------------------+
metadata
+-------------+-----------+---------------------------+
| config_type | config_id | description |
+-------------+-----------+---------------------------+
| a | foo | Type a config with id foo |
| a | bar | Type a config with id bar |
| b | foo | Type b config with id foo |
| a | baz | Type a config with id baz |
+-------------+-----------+---------------------------+
我正在尝试获取以下输出(给定 authUserEmail
的 john.smith@gmail.com
):
+-------------+-----------+---------------------------+
| config_type | config_id | description |
+-------------+-----------+---------------------------+
| a | foo | Type a config with id foo |
| a | bar | Type a config with id foo |
| b | foo | Type b config with id foo |
| a | baz | Type a config with id baz |
+-------------+-----------+---------------------------+
我不是 SQL 专家,但我通常知道在这里一起使用 SELECT
和 DISTINCT
是行不通的。正确的做法是什么?
【问题讨论】:
您的意思是您只希望每个配置 ID/类型返回一行吗?也许提供最小的输入/输出示例只是为了阐明要求? 正确。我会将其添加到原始问题中。 听起来你想要groupBy
,在这种情况下,你需要聚合选择列表中的剩余列。可能只获取最新的(最大)时间戳就可以了……
不完全确定我是否遵循。提供的输入/输出是否有助于澄清我的问题?
我想是的,会玩得很开心的......
【参考方案1】:
以下内容大致适合您吗?我确实使用了 as,因此我们可以获取 10 个最新配置 (max(timestamp)..group by config
),然后在最终投影中删除时间戳列。请注意,最终记录可能不会以确切的时间戳顺序出现,因为您不希望在最终输出中出现时间戳,但它们将是最近的 10 个。我没有添加 DELETED
列,但假设您将根据问题中的代码重新添加它。
knex.with('ordered_items', (qb) =>
qb.table('metadata')
.innerJoin('view_events', function()
this.on('metadata.config_id', '=', 'view_events.config_id')
.andOn('metadata.config_type', '=', 'view_events.config_type')
)
.where('view_events.email': 'john.smith@gmail.com')
.select(['metadata.config_type', 'metadata.config_id',
'metadata.description'])
.max('view_events.timestamp', as: 'max_ts')
.groupBy(['metadata.config_id', 'metadata.config_type', 'metadata.description'])
.orderBy('max_ts', 'desc')
.limit(10))
.table('ordered_items')
.select(['config_type', 'config_id', 'description'])
我的输入输出:
sqlite> select * from metadata;
a|foo|Type a config with id foo
a|bar|Type a config with id bar
b|foo|Type b config with id foo
a|baz|Type a config with id baz
sqlite> select * from view_events;
a|foo|2020-01-23T03:08:14.618Z|john.smith@gmail.com
a|foo|2020-01-23T03:08:14.500Z|jane.doe@gmail.com
a|foo|2020-01-23T03:08:13.618Z|john.smith@gmail.com
a|bar|2020-01-23T03:08:12.618Z|john.smith@gmail.com
a|foo|2020-01-23T03:08:11.618Z|john.smith@gmail.com
b|foo|2020-01-23T03:08:10.618Z|john.smith@gmail.com
a|baz|2020-01-23T03:08:09.618Z|john.smith@gmail.com
a|foo|2020-01-23T03:08:08.618Z|john.smith@gmail.com
[ config_type: 'a',
config_id: 'foo',
description: 'Type a config with id foo' ,
config_type: 'a',
config_id: 'bar',
description: 'Type a config with id bar' ,
config_type: 'b',
config_id: 'foo',
description: 'Type b config with id foo' ,
config_type: 'a',
config_id: 'baz',
description: 'Type a config with id baz' ]
【讨论】:
"with \"ordered_events\" as (select max(\"view_events\".\"timestamp\") from \"metadata\" inner join \"view_events\" on \"metadata\ ".\"storage_id\" = \"view_events\".\"config_storage_id\" where \"view_events\".\"user\" = $1 and \"metadata\".\"deleted\" = $2 group by \ "metadata\".\"config_id\", \"metadata\".\"config_type\", \"metadata\".\"description\" order by \"view_events\".\"timestamp\" desc 限制 $3 ) select \"config_id\", \"config_type\", \"description\" from \"ordered_events\" - 列 \"view_events.timestamp\" 必须出现在 GROUP BY 子句中或用于聚合函数" 我不知道这是否重要,但我使用的是 Postgres,而您的解决方案看起来是针对 sqlite 运行的。 Mmsqlite3
是相当宽容。我已经尝试根据错误进行修复,手指交叉它可以工作,但如果不是我可以翻转到 postgres 并在那里修复它。告诉我。
做到了!标记为已解决。不过很好奇,如果添加“as”子句来修复初始错误呢?
太棒了——谢谢。与其说是修复的as
,不如说是通过max(timestamp)
而不是timestamp
排序,后者是无效的sql,但sqlite3
允许通过。为了按max(timestamp)
排序,添加别名似乎更容易,因此添加了as
。希望这很清楚。以上是关于Knex.js INNER JOIN 结果的 DISTINCT的主要内容,如果未能解决你的问题,请参考以下文章
SQL中inner join,outer join和cross join的区别
Knex.js:加入 'select' 和 'where' 子句
Inner Join, Left Outer Join和Association的区别