带有 Knex.js 的 select 语句的子查询

Posted

技术标签:

【中文标题】带有 Knex.js 的 select 语句的子查询【英文标题】:Subquery of select statement with Knex.js 【发布时间】:2017-07-11 01:44:35 【问题描述】:

我正在尝试使用 Knex 创建以下带有子查询的查询:

SELECT 
  t.*, 
  (SELECT COUNT(*) FROM team_users tu WHERE TeamID = t.ID) AS UserCount,
  (SELECT COUNT(*) FROM team_access ta WHERE TeamID = t.ID) AS AppCount
FROM teams t WHERE OwnerUserID = _UserID;

结果应该是包含来自不同表(team_users、team_access)的 UserCount 和 AppCount 的计数聚合的团队表

id | Name      | OwnerUserID   | UserCount | AppCount
-----------------------------------------------------
134| Team A    | 1538          | 7         | 6
135| Team B    | 1538          | 4         | 2
136| Team C    | 1538          | 12        | 1

我认为等效的 knex 实现是:

var subquery1 = Knex.knex('team_users').count('*').where('TeamID', 'teams.ID').as('UserCount');
var subquery2 = Knex.knex('team_access').count('*').where('TeamID', 'teams.ID').as('AppCount');
Knex.knex.select('*', subquery1, subquery2).from('teams').where("OwnerUserID", ownerId).asCallback(dataSetCallback);

运行它,我确实在返回的对象中获得了“UserCount”和“AppCount”列,但始终为零,可能是因为它没有识别子查询中的“teams.ID”。

我设法使用 Knex.raw 函数解决了这个问题:

Knex.knex('teams')
    .select('*', Knex.knex.raw('(SELECT COUNT(*) FROM team_users WHERE TeamID = teams.ID) AS UserCount'), Knex.knex.raw('(SELECT COUNT(*) FROM team_access WHERE TeamID = teams.ID) AS AppCount'))
    .where("OwnerUserID", ownerId)
    .asCallback(dataSetCallback);

但我很想知道如何使用子查询对象来实现这一点。

【问题讨论】:

【参考方案1】:

您正在尝试将teams.ID 字符串作为值传递。为了能够做到.where('columnName', 'otherColumnName'),必须使用knex.ref 来传递otherColumnName 作为标识符。

var teamsIdColumnIdentifier = knex.ref('teams.ID'); // <-- [1]

var subquery1 = Knex.knex('team_users').count('*')
  .where('TeamID', teamsIdColumnIdentifier).as('UserCount');
var subquery2 = Knex.knex('team_access').count('*')
  .where('TeamID', teamsIdColumnIdentifier).as('AppCount');

Knex.knex.select('*', subquery1, subquery2).from('teams')
  .where("OwnerUserID", ownerId).asCallback(dataSetCallback);

[1]在May 2018中将knex.ref添加到Knex之前,你必须使用knex.raw,像这样;

var teamsIdColumnIdentifier = knex.raw('??', ['teams.ID']);

【讨论】:

以上是关于带有 Knex.js 的 select 语句的子查询的主要内容,如果未能解决你的问题,请参考以下文章

SELECT中(非常)常用的子查询操作

Knex.js:加入 'select' 和 'where' 子句

如何在Knex中扩展QueryBuilder类时访问当前上下文的事务

第四章 MySQL高级查询

如何在 Knex JS 中使用 IS NOT NULL

Knex.js 和 MySQL:将整数转换为布尔值以进行批量选择