最新消息:雨落星辰是一个专注网站SEO优化、网站SEO诊断、搜索引擎研究、网络营销推广、网站策划运营及站长类的自媒体原创博客

SELECT SQL subquery to return an entire dataset as a single column - Stack Overflow

programmeradmin1浏览0评论

Say I have a table of entries and a 2nd table of related entries:

Table 1:

id data
1 foo
2 bar

Say I have a table of entries and a 2nd table of related entries:

Table 1:

id data
1 foo
2 bar

Table 2:

id related_to data
1 1 ding
2 1 dong

Is there a way to write a query that returns data like this:

[
  { 
    id: 1,
    data: foo,
    related_stuff: [
      { 
        id: 1,
        data: ding
      },
      { 
        id: 2,
        data: dong
      }
    ]
  },
  {
    id: 2,
    data: bar,
    related_stuff: []
  }
]

There are aggregate functions that combine single entries together to one aggregated list, mostly returned as strings, but I just want the entire dataset as an object (I work with JS mostly but the kind of language the database is bound to should be irrelevant here).

Example:

SELECT t1.id as id, t1.data as data, COUNT(t2.id) as amount
FROM t1
  LEFT JOIN t2 ON t1.id = t2.related_to
GROUP BY t1.id

I would think something akin to this:

SELECT t1.id as id,
       t1.data as data,
       SELECT * FROM t2
           WHERE t2.related_to = t1.id
       as related_stuff
FROM t1

But I get an error that entries from the subquery must have at most 1 column.

Right now our code simply queries the entries of Table 1 and then does a query to Table 2 for each entry returned by Table 1 but I feel like there should be a way to write a single SQL query that returns the entirety in one query.

Share Improve this question edited Feb 10 at 12:50 Lelio Faieta 6,6849 gold badges47 silver badges84 bronze badges asked Feb 10 at 12:25 salbeirasalbeira 2,6115 gold badges28 silver badges44 bronze badges 7
  • 3 Which dbms are you using? – jarlh Commented Feb 10 at 12:29
  • I felt like this is such a basic thing to do I would have guessed that does not matter. I work with Postgres and sqlite at the moment but I feel like this is so basic it should have a common syntax for all backends. – salbeira Commented Feb 10 at 12:30
  • 2 The simple answer is that SQL is not designed that way. A query can return a table consisting of columns of simple data types (int, string, date, ...), but not columns containing nested tables. Depending on the DBMS you are using, there may be ways to work around that (JSON typically, sometimes cursors or objects). – Thorsten Kettner Commented Feb 10 at 13:01
  • Usually the idea is to either do one big query. Or to use two separate queries, where the second returns the parent PKs of the first query (no need to do N second queries, just a single query with the same WHERE). In both cases, use a hashmap/dictionary to put related_stuff columns into a nested child list. SQL just doesn't work the way you are thinking, that's more like an object database such as MongoDB. – Charlieface Commented Feb 10 at 13:04
  • @Charlieface So do I understand correctly I would do two queries and just fiddle together the data afterwards myself into the correct t1 object? Is that quicker than doing individual SQL queries? I would still need to do n x m compares with the results. – salbeira Commented Feb 10 at 13:15
 |  Show 2 more comments

1 Answer 1

Reset to default 1

Actually, it would vary based on the dbms.

For postgres you can try using json_agg in this way.

SELECT 
    t1.id, 
    t1.data, 
    COALESCE(json_agg(
        json_build_object('id', t2.id, 'data', t2.data)
    ) FILTER (WHERE t2.id IS NOT NULL), '[]') AS related_stuff
FROM table1 t1
LEFT JOIN table2 t2 ON t1.id = t2.related_to
GROUP BY t1.id, t1.data;

But, this wouldn't work with sqlite, that has something called json_group_array.

发布评论

评论列表(0)

  1. 暂无评论
ok 不同模板 switch ($forum['model']) { /*case '0': include _include(APP_PATH . 'view/htm/read.htm'); break;*/ default: include _include(theme_load('read', $fid)); break; } } break; case '10': // 主题外链 / thread external link http_location(htmlspecialchars_decode(trim($thread['description']))); break; case '11': // 单页 / single page $attachlist = array(); $imagelist = array(); $thread['filelist'] = array(); $threadlist = NULL; $thread['files'] > 0 and list($attachlist, $imagelist, $thread['filelist']) = well_attach_find_by_tid($tid); $data = data_read_cache($tid); empty($data) and message(-1, lang('data_malformation')); $tidlist = $forum['threads'] ? page_find_by_fid($fid, $page, $pagesize) : NULL; if ($tidlist) { $tidarr = arrlist_values($tidlist, 'tid'); $threadlist = well_thread_find($tidarr, $pagesize); // 按之前tidlist排序 $threadlist = array2_sort_key($threadlist, $tidlist, 'tid'); } $allowpost = forum_access_user($fid, $gid, 'allowpost'); $allowupdate = forum_access_mod($fid, $gid, 'allowupdate'); $allowdelete = forum_access_mod($fid, $gid, 'allowdelete'); $access = array('allowpost' => $allowpost, 'allowupdate' => $allowupdate, 'allowdelete' => $allowdelete); $header['title'] = $thread['subject']; $header['mobile_link'] = $thread['url']; $header['keywords'] = $thread['keyword'] ? $thread['keyword'] : $thread['subject']; $header['description'] = $thread['description'] ? $thread['description'] : $thread['brief']; $_SESSION['fid'] = $fid; if ($ajax) { empty($conf['api_on']) and message(0, lang('closed')); $apilist['header'] = $header; $apilist['extra'] = $extra; $apilist['access'] = $access; $apilist['thread'] = well_thread_safe_info($thread); $apilist['thread_data'] = $data; $apilist['forum'] = $forum; $apilist['imagelist'] = $imagelist; $apilist['filelist'] = $thread['filelist']; $apilist['threadlist'] = $threadlist; message(0, $apilist); } else { include _include(theme_load('single_page', $fid)); } break; default: message(-1, lang('data_malformation')); break; } ?>