将嵌套循环查询组合到父数组结果 - pg-promise

Posted

技术标签:

【中文标题】将嵌套循环查询组合到父数组结果 - pg-promise【英文标题】:Combine nested loop queries to parent array result - pg-promise 【发布时间】:2018-10-27 17:39:36 【问题描述】:

我是 node(express) 和 pg-promise 的新手,还无法弄清楚如何将每个嵌套查询(循环)的结果添加到主 json 数组结果查询中。

我有两个表:Posts 和 cmets。

CREATE TABLE post(
id serial,
content text not null,
linkExterno text,
usuario VARCHAR(50) NOT NULL REFERENCES usuarios(alias) ON UPDATE cascade ON DELETE cascade,
multimedia text,
ubicacation VARCHAR(100),
likes integer default 0,
time VARCHAR default now(),
reported boolean default false,
PRIMARY KEY (id)  );

CREATE TABLE comment(
id serial,
idPost integer NOT NULL REFERENCES post(id) ON UPDATE cascade ON DELETE cascade,
acount VARCHAR(50) NOT NULL REFERENCES users(alias) ON UPDATE cascade ON DELETE cascade,
content text NOT NULL,
date date default now(),
PRIMARY KEY (id));

所以我想将每个 cmets 的结果添加到每个帖子并返回帖子。 我有这个,但不起作用:

con.task(t => 
    return t.any('select *, avatar from post, users where user= $1 and user = alias ORDER BY time DESC LIMIT 10 OFFSET $2', [username, pos])
    .then(posts => 
        if(posts.length > 0)
            for (var post of posts)
                post.coments = t.any('select * from comment where idPost = $1 ', post.id);
            
        
    );
).then(posts => 
    res.send(posts);
).catch(error => 
    console.log(error);
);

有什么建议吗? PD:我认为我的问题有点类似于这个: get JOIN table as array of results with PostgreSQL/NodeJS

答案:

选项 1(最佳选择)

通过 JSON 到 psql 进行单个查询(JSON 查询

查看@vitaly-t 的回答

使用 ajax 异步获取嵌套数据。

选项 2

function buildTree(t) 
        return t.map("select *, avatar from publicacion, usuarios where usuario = $1 and usuario = alias ORDER BY hora DESC LIMIT 10 OFFSET $2", [username, cantidad], posts => 
                return t.any('select * from comentario where idPublicacion = $1', posts.id)
                    .then(coments => 
                        posts.coments = coments;
                        console.log(posts.coments);
                        return posts;
                    );
        ).then(t.batch); // settles the array of generated promises
    

    router.get('/publicaciones', function (req, res) 
        cantidad = req.query.cantidad || 0; //num de publicaciones que hay
        username = req.session.user.alias;

        con.task(buildTree)
        .then(data => 
            res.send(data);
        )
        .catch(error => 
            console.log(error);
        );
    );

选项 3(异步)

try
    var posts = await con.any('select *, avatar from post, users where user = $1 and user = alias ORDER BY time DESC LIMIT 10 OFFSET $2', [username, q])
    for (var post of posts)
        post.coments = await con.any('select * from comment where idPublictcion = $1', post.id);
    
catch(e)
    console.log(e);

【问题讨论】:

实际上,最好的答案是这里通过 JSON 查询的第二个选项:***.com/questions/39805736/…,因为它比其他任何东西都快得多,并且可以很好地扩展。并且await 代码在缩放方面将是最差的,因为它是同步的;) @vitaly-t 你说得对!谢谢你的时间! :) 查看更新,您可能会更喜欢它;) 【参考方案1】:

您可以使用await,但它会同步工作。

return t.any('select *, avatar from post, users where user= $1 and user = alias ORDER BY time DESC LIMIT 10 OFFSET $2', [username, pos])
    .then(posts => 
        if(posts.length > 0)
            for (var post of posts)
                post.coments = await t.any('select * from comment where idPost = ', post.id);
            
        
        return posts;
    );

其实我推荐你使用像bookshelfknextypeorm

这样的orm工具

【讨论】:

使用 pg-promise 的解决方案将大大优于任何提到的 ORM-s,因此不是一个好的建议。 感谢您提供的精彩模块 vitaly-t ;)【参考方案2】:

我是pg-promise 的作者;)


con.task(t => 
    const a = post => t.any('SELECT * FROM comment WHERE idPost = $1', post.id)
        .then(comments => 
            post.comments = comments;
            return post;
        );
    return t.map('SELECT *, avatar FROM post, users WHERE user = $1 AND user = alias ORDER BY time DESC LIMIT 10 OFFSET $2', [username, pos], a)
        .then(t.batch);
)
    .then(posts => 
        res.send(posts);
    )
    .catch(error => 
        console.log(error);
    );

另请参阅此问题:get JOIN table as array of results with PostgreSQL/NodeJS。

更新

如果您不想一直使用 JSON 查询方法,那么以下将比原始解决方案更好地扩展,因为我们连接所有子查询,然后将它们作为一个查询执行:

con.task(async t => 
    const posts = await t.any('SELECT *, avatar FROM post, users WHERE user = $1 AND user = alias ORDER BY time DESC LIMIT 10 OFFSET $2', [username, pos]);
    const a = post => (query: 'SELECT * FROM comment WHERE idPost = $id', values: post);
    const queries = pgp.helpers.concat(posts.map(a));
    await t.multi(queries)
        .then(comments => 
            posts.forEach((p, index) => 
                p.comments = comments[index];
            );
        );
    return posts;
)
    .then(posts => 
        res.send(posts);
    )
    .catch(error => 
        console.log(error);
    );

查看 API:

helpers.concat Database.multi

【讨论】:

不错的更新!问题是我的路由文件中没有 pgp 对象,只有连接实例,但无论如何都有很好的贡献。 @SergioRey 这听起来不对。如果你整理图书馆the way you should,就不会有这个问题了。 当您必须组合两个以上的查询时如何实现? @NaveenGeorgeThoppan 这完全取决于查询逻辑/关系。无法笼统地回答。 在与上述相同的情况下,一个帖子可以有与之关联的投票。如何添加查询以获取投票以及帖子?所以会有两个查询来获得 cmets 和 votes。你能帮我添加另一个查询的语法吗?就我而言,我要添加 5 或 6 个这样的查询。【参考方案3】:

如果您想要结构化(嵌套)数据,而不必

A) 使用 json 函数重写您的 sql,或将其拆分为多个任务查询,或者

B) 重构代码以使用重型 ORM 的 API

您可以查看sql-toolkit。这是一个为pg-promise 构建的节点库,它允许您编写常规的本机 SQL 并接收正确结构化(嵌套)的纯业务对象。严格来说是 pg-promise 之上的一个增强工具包,并不寻求抽象出 pg-promise(你还是设置了 pg-promise 就可以直接使用了)。

例如:

class Article extends BaseDAO 
  getBySlug(slug) 
    const query = `
      SELECT
        $Article.getSQLSelectClause(),
        $Person.getSQLSelectClause(),
        $ArticleTag.getSQLSelectClause(),
        $Tag.getSQLSelectClause()
      FROM article
      JOIN person
        ON article.author_id = person.id
      LEFT JOIN article_tags
        ON article.id = article_tags.article_id
      LEFT JOIN tag
        ON article_tags.tag_id = tag.id
      WHERE article.slug = $(slug);
  `;
  return this.one(query,  slug );
  // OUTPUT: Article person: Person, tags: Tags[Tag, Tag, Tag]

select 子句使用业务对象“getSQLSelectClause”方法来节省键入列时的乏味,并确保名称没有冲突(没有什么神奇的事情,可以直接写出来)。

this.one 是对sql-toolkits 基本 DAO 类的调用。它负责将平面结果记录构造成一个很好的嵌套结构。

(还要注意它是“一”,它与我们的 SQL 心智模型相匹配。用于 one、oneOrNone、many 和 any 的 DAO 方法确保它们对生成的***业务对象的数量进行计数 - 而不是sql 表达式返回的行!)

查看repository,了解如何在pg-promise 之上设置它的详细信息。 (声明,我是sql-toolkit的作者。)

【讨论】:

以上是关于将嵌套循环查询组合到父数组结果 - pg-promise的主要内容,如果未能解决你的问题,请参考以下文章

Codeigniter 将查询组合在一个循环中

如何在foreach循环中将数组结果组合在php中

使用嵌套循环迭代 m^n 组合[关闭]

使用嵌套的每个循环构建多维数组?

php foreach 嵌套循环大数组很慢?

与数组 C++ 组合