在多个表

时间:2015-11-29 18:34:48

标签: javascript sql node.js postgresql

我有下面的表格,我试着找到如何 获得结果是文章ID数组或文章行, 同时匹配标签' hiphop'' rock'' single'在ArticleTag0ArticleTag1表中。

现在我使用下面的代码,
将每个文章ID数组标记为不同的标记类型,

例如,如果文章x Tag0中标记的文章ID行为[0,2,4,6]且文章x Tag1为[0,4,9],则 然后比较每个数组得到的数字都在上面两个数组get,[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")
    );

用户输入参数
inputGenres(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

1 个答案:

答案 0 :(得分:1)

通常,使用一个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')来绑定变量($i)。 &#39;所有&#39;标签选项可以通过用exists(...)替换相应的true块来完成。

但我建议你稍微重新设计你的架构。如何用数组表示文章的标签?

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. 可以使用具有索引支持的数组的重叠操作。
  4. 让我们插入一些值:

    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)
    

    现在查询尽可能简单明了(&&运算符意味着重叠&#39;):

    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 * from article where genres && $1 and releases && $2;并为$1$2生成适当的字符串。

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

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