SQL选择书籍格式

时间:2019-04-11 00:23:21

标签: sql postgresql

我最近遇到了一个Postgres SQL select测试问题,这个问题我没有看到,也很难找到正确的答案。一个表基本上包含一本书的页码和页面标题,并且对象是创建一个SELECT来对本书进行布局,如下所示:

  

左页,右页

其中left_page包含所有偶数页号,right_page包含所有奇数页号,但是其中0页显示空值,而任何非连续页都将具有空值。可以有空白页,因此页码可以跳过。

我的猜测是使用滞后或超前功能按顺序查找下一页,然后查看它是否连续,但是遇到问题。

这是一个例子:

create table mysharkbook (page_no integer not null, title varchar(30) not null, unique(page_no));
insert into mysharkbook (page_no,title) VALUES (1,'Hammerhead');
insert into mysharkbook (page_no,title) VALUES (2,'Great White');
insert into mysharkbook (page_no,title) VALUES (3,'Blue');
insert into mysharkbook (page_no,title) VALUES (4,'Tiger');
insert into mysharkbook (page_no,title) VALUES (6,'Blacktip');
select * from mysharkbook;

结果

1   "Hammerhead"
2   "Great White"
3   "Blue"
4   "Tiger"
6   "Blacktip"

现在,尝试使SELECT这样列出:

left_page, right_page
null        | Hammerhead
Great White | Blue
Tiger       | null
Blacktip    | null

这是我(可怜的)尝试:

SELECT CASE WHEN prev_page IS NULL OR prev_page <> page_no - 1 OR (page_no % 2) = 0 THEN NULL ELSE title END As left_title,
CASE WHEN (page_no % 2) = 1 THEN title ELSE NULL END As right_title
FROM
(
SELECT LAG (page_no, 1) OVER (
 ORDER BY
 page_no
 ) AS prev_page,
title,page_no
from mysharkbook
order by page_no
) d

有什么最好的想法吗?

6 个答案:

答案 0 :(得分:1)

完整的子查询联接可能会获得奇数页和偶数页。对于奇数页,我们还附加了generate_series()来获取可能丢失的页面。

SELECT even.title left_page,
       odd.title right_page
       FROM (SELECT *
                    FROM mysharkbook b
                    WHERE b.page_no % 2 = 0) even
            FULL JOIN (SELECT gs.pn page_no,
                              b.title
                              FROM generate_series(1,
                                                   (SELECT max(b.page_no)
                                                           FROM mysharkbook b),
                                                   2) gs (pn)
                                   LEFT JOIN mysharkbook b
                                             ON b.page_no = gs.pn) odd
                      ON odd.page_no = even.page_no + 1
       ORDER BY odd.page_no;

db<>fiddle

(P.S .:我喜欢以鲨鱼为样本的想法!不是一次又一次地使用水果。)

答案 1 :(得分:1)

我相信这就是您想要的;

WITH cte
AS (
    SELECT generate_series(0, (
                SELECT max(page_no) max_pages
                FROM mysharkbook
                )) AS page
    )
SELECT cte.page
    ,l.title l_title
    ,r.title r_title
FROM cte
LEFT OUTER JOIN mysharkbook l ON l.page_no = cte.page
LEFT OUTER JOIN mysharkbook r ON r.page_no = cte.page + 1
WHERE mod(page, 2) = 0
ORDER BY page

也许有更好的方法。

-HTH

答案 2 :(得分:1)

另一个:

select distinct on (page.no / 2)
    msb.title, lead(msb.title) over(order by page.no)
from generate_series(0, (select max(page_no) from  mysharkbook)) page(no) 
left join mysharkbook msb on page.no = msb.page_no
order by page.no / 2

输出:

enter image description here

答案 3 :(得分:1)

另一种方法是将页面标题放入两个元素的数组中

select 
    page_titles[1] as left_page, page_titles[2] as right_page
from
(
    select 
        page.no / 2 as even_pages, array_agg(title order by page.no) as page_titles
    from generate_series(0, (select max(page_no) from  mysharkbook)) page(no) 
    left join mysharkbook msb on page.no = msb.page_no
    group by even_pages
) t

enter image description here

答案 4 :(得分:1)

单次加入和分组依据的另一种方法。

使用最小/最大无关紧要。该技术是使用模2取偶数和奇数页。如果我没记错的话,这模拟了Oracle的MIN + KEEP DENSE_RANK组合。

select 
    min(title) filter(where page.no % 2 = 0) as left_page,
    min(title) filter(where page.no % 2 = 1) as right_page,
    max(title) filter(where page.no % 2 = 0) as left_page_x,
    max(title) filter(where page.no % 2 = 1) as right_page_x                           
from generate_series(0, (select max(page_no) from  mysharkbook)) page(no) 
left join mysharkbook msb on page.no = msb.page_no
group by page.no / 2
order by page.no / 2                           

说明:

enter image description here

答案 5 :(得分:0)

由于页码可以跳过,因此生成从0到max(page_no)的序列将产生完全空白的结果(左侧为null,右侧为null)。

如何将记录集分为偶数和奇数堆,并按顺序将2个记录集连接在一起->左页+ 1 =右页。

-- Test accepted solution by an additional row
insert into mysharkbook (page_no,title) VALUES (10,'Baby shark');

SELECT ls.title, rs.title
FROM
(SELECT * FROM  mysharkbook WHERE mod(page_no,2) = 0) as ls
FULL OUTER JOIN
(SELECT * FROM  mysharkbook WHERE mod(page_no,2) <> 0) as rs
ON ls.page_no + 1 = rs.page_no
ORDER BY COALESCE(ls.page_no, rs.page_no);

但这确实需要在同一张桌子上进行2次传球-也许不是那么出色。