javascript - 在多个表中同时获取结果

标签 javascript sql node.js postgresql

我有如下表,我尝试找到如何 得到的结果是文章id数组或文章行, 同时匹配 ArticleTag0ArticleTag1 表中的标签“hiphop”、“rock”、“single”。

现在我使用下面的代码,
获取用不同标签类型标记的每篇文章 id 数组,

例如,如果文章 x Tag0 中标记的文章 ID 行为 [0, 2, 4, 6],文章 x Tag1 为 [0, 4, 9],
然后比较每个数组得到上面两个数组中的数字,[0, 4]。

我想知道是否有更好的查询一次选择它们?考虑性能

表格

    CREATE TABLE IF NOT EXISTS "Article"(
    "ArticleId" SERIAL NOT NULL,
    "PublishDate" timestamp without time zone,
    "Active" bit NOT NULL,
    PRIMARY KEY ("ArticleId")
    );

    CREATE TABLE IF NOT EXISTS "Tag0"(
    "TagId" SERIAL NOT NULL,
    "Name" varchar,
    "Active" bit NOT NULL,
    PRIMARY KEY ("TagId")
    );

    CREATE TABLE IF NOT EXISTS "Tag1"(
    "TagId" SERIAL NOT NULL,
    "Name" varchar,
    "Active" bit NOT NULL,
    PRIMARY KEY ("TagId")
    );


    CREATE TABLE IF NOT EXISTS "ArticleTag0"(
    "ArticleTagId" SERIAL NOT NULL,
    "ArticleId" integer NOT NULL,
    "TagId" integer NOT NULL,
    FOREIGN KEY ("ArticleId") REFERENCES "Article" ("ArticleId") ON DELETE CASCADE ON UPDATE CASCADE,
    FOREIGN KEY ("TagId") REFERENCES "Tag0" ("TagId") ON DELETE CASCADE ON UPDATE CASCADE,
    PRIMARY KEY ("ArticleTagId")
    );

    CREATE TABLE IF NOT EXISTS "ArticleTag1"(
    "ArticleTagId" SERIAL NOT NULL,
    "ArticleId" integer NOT NULL,
    "TagId" integer NOT NULL,
    FOREIGN KEY ("ArticleId") REFERENCES "Article" ("ArticleId") ON DELETE CASCADE ON UPDATE CASCADE,
    FOREIGN KEY ("TagId") REFERENCES "Tag1" ("TagId") ON DELETE CASCADE ON UPDATE CASCADE,
    PRIMARY KEY ("ArticleTagId")
    );

代码

用户输入参数
输入流派 (Tag0) - [ 'hiphop', 'rock' ]
inputReleaseType (Tag1) - [ 'single' ]

    ... 
    // inputGenres
    var inputGenresArticleIdList = [];

    if (inputGenres[0] == 'all') {
      var query = 'SELECT * FROM "Article"';
      var params = [];

      var selectArticle = yield crudDatabase(db,query,params);
      if (typeof selectArticle.error !== 'undefined') {
        response.meta.code = '500';
      } else {
        for (var i = 0; i < selectArticle.result.rows.length; i++) {
          inputGenresArticleIdList.push(selectArticle.result.rows[i].ArticleId);
        }
      }
    } else {
      var query = 'SELECT DISTINCT ON ("ArticleId") * FROM "ArticleTag0" LEFT OUTER JOIN "Tag0" ON ("ArticleTag0"."TagId" = "Tag0"."TagId") WHERE "Name" IN (';
      for (var i = 0; i < inputGenres.length; i++) {
        if (i > 0) {
          query += ',';
        }
        query += '$' + (i + 1);
      }
      query += ')';
      var params = inputGenres;

      var selectArticleTag0 = yield crudDatabase(db,query,params);
      if (typeof selectArticleTag0.error !== 'undefined') {
        response.meta.code = '500';
      } else {
        for (var i = 0; i < selectArticleTag0.result.rows.length; i++) {
          inputGenresArticleIdList.push(selectArticleTag0.result.rows[i].ArticleId);
        }
      }
    }
    console.log(inputGenresArticleIdList);
    // end: inputGenres


    // inputReleaseType
    var inputReleaseTypeArticleIdList = [];

    if (inputReleaseType[0] == 'all') {
      var query = 'SELECT * FROM "Article"';
      var params = [];

      var selectArticle = yield crudDatabase(db,query,params);
      if (typeof selectArticle.error !== 'undefined') {
        response.meta.code = '500';
      } else {
        for (var i = 0; i < selectArticle.result.rows.length; i++) {
          inputReleaseTypeArticleIdList.push(selectArticle.result.rows[i].ArticleId);
        }
      }
    } else {
      var query = 'SELECT DISTINCT ON ("ArticleId") * FROM "ArticleTag4" LEFT OUTER JOIN "Tag4" ON ("ArticleTag4"."TagId" = "Tag4"."TagId") WHERE "Name" IN (';
      for (var i = 0; i < inputReleaseType.length; i++) {
        if (i > 0) {
          query += ',';
        }
        query += '$' + (i + 1);
      }
      query += ')';
      var params = inputReleaseType;

      var selectArticleTag4 = yield crudDatabase(db,query,params);
      if (typeof selectArticleTag4.error !== 'undefined') {
        response.meta.code = '500';
      } else {
        for (var i = 0; i < selectArticleTag4.result.rows.length; i++) {
          inputReleaseTypeArticleIdList.push(selectArticleTag4.result.rows[i].ArticleId);
        }
      }
    }
    console.log(inputReleaseTypeArticleIdList);
    // end: inputReleaseType

    ... then loop each array and compare

最佳答案

一般来说,最好使用一条 SQL 语句来完成这项工作:它不太复杂,可以节省与服务器之间的往返次数,并且可以利用数据库的算法和优化。

您可以使用以下语句获得您想要的结果:

select *
from   "Article" a
where  exists (
         select 1
         from   "ArticleTag0" at0,
                "Tag0" t0
         where  at0."ArticleId" = a."ArticleId"
         and    t0."TagId" = at0."TagId" 
         and    t0."Name" in ('hiphop','rock')
       )
and    exists (
         select 1
         from   "ArticleTag1" at1, 
                "Tag1" t1 
         where  at1."ArticleId" = a."ArticleId"
         and    t1."TagId" = at1."TagId" 
         and    t1."Name" in ('single')
       );

当然,您仍然需要更改文字(例如'hiphop')来绑定(bind)变量($i)。标签的“全部”选项可以通过用 true 替换适当的 exists(...) block 来完成。

<小时/>

但我建议对您的架构进行一些重新设计。用数组表示文章的标签怎么样?

create table article(
  articleId   serial primary key,
  publishDate timestamp without time zone,
  active      boolean, -- clearer that 'bit'
  genres      text[],  -- array of genre tags
  releases    text[]   -- array of release tags
);

好处:

  1. 文章和标签之间不需要中间表(例如 ArticleTag0)。
  2. 更简单的查询。
  3. 可以使用具有索引支持的数组重叠操作。

让我们插入一些值:

tags=# insert into article(publishDate,active,genres,releases) values ('2015-09-01',true,'{"hiphop"}','{"single"}');
INSERT 0 1
tags=# insert into article(publishDate,active,genres,releases) values ('2015-10-01',true,'{"rock","blues"}','{"album"}');
INSERT 0 1
tags=# insert into article(publishDate,active,genres,releases) values ('2015-11-01',true,'{"pop"}','{"ep"}');
INSERT 0 1

tags=# select * from article;
 articleid |     publishdate     | active |    genres    | releases 
-----------+---------------------+--------+--------------+----------
         1 | 2015-09-01 00:00:00 | t      | {hiphop}     | {single}
         2 | 2015-10-01 00:00:00 | t      | {rock,blues} | {album}
         3 | 2015-11-01 00:00:00 | t      | {pop}        | {ep}
(3 rows)

现在的查询尽可能简单明了(&& 运算符表示“重叠”):

tags=# select * from article where genres && '{"hiphop","rock"}' and releases && '{"single"}';
 articleid |     publishdate     | active |  genres  | releases 
-----------+---------------------+--------+----------+----------
         1 | 2015-09-01 00:00:00 | t      | {hiphop} | {single}
(1 row)

它还简化了查询文本的构造:使用 select * fromarticle where Genels && $1 andreleases && $2; 并为 $1 生成适当的字符串$2.

为了加快查询速度,您可以创建两个支持数组 && 运算符的 GIN 索引:

create index on article using gin(genres);
create index on article using gin(releases);

关于javascript - 在多个表中同时获取结果,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/33986439/

相关文章:

ios - sqlite3_step 返回 SQLITE_CONSTRAINT

sql - postgresql : Get only a single record from result containing multiple rows

sql - UPDATE 语句在存储过程中不起作用;没有错误,也没有发生异常

node.js - 解析expressjs中的url参数 - angularjs应用程序

javascript - 无法让 JavaScript 检查空对象

javascript - 获取文档的宽度和高度

javascript - 如何让 JavaScript 文件在 Rails 5 应用程序上运行

javascript - 我的下拉菜单压低了其他所有内容

javascript - 我如何使用 Spies 测试我的异步 jasmine/nodejs/promise 代码

javascript - 我无法让 MEAN JS 创建新客户