Select only partial result but get total number of rows

南楼画角 提交于 2021-02-11 18:22:43

问题


I got stuck on SQL subquery selection. Right now, I have a table products:

 id | name  |     description      
----+-------+----------------------
  6 | 123   | this is a           +
    |       | girl.
  7 | 124   | this is a           +
    |       | girl.
  8 | 125   | this is a           +
    |       | girl.
  9 | 126   | this is a           +
    |       | girl.
 10 | 127   | this is a           +
    |       | girl.
 11 | 127   | this is a           +
    |       | girl. Isn't this?
 12 | 345   | this is a cute slair
 13 | ggg   | this is a           +
    |       | girl
 14 | shout | this is a monster
 15 | haha  | they are cute
 16 | 123   | this is cute

What I want to do is to find ( the total number of records and the first 5 records ) which contains '1' or 'this' in either name or description columns.

What I can figure out is so ugly:

SELECT *, (select count(id)
           from (SELECT * from products
                 where description like any (array['%1%','%this%'])
                           or name like any (array['%1%','%this%'])
                ) as foo
          ) as total
from (SELECT * from products
      where description like any (array['%1%','%this%'])
                or name like any (array['%1%','%this%']))
     ) as fooo
limit 5;

回答1:


You can use the aggregate function count() as window function to compute the total count in the same query level:

SELECT id, name, description, count(*) OVER () AS total
FROM   products p
WHERE  description LIKE ANY ('{%1%,%this%}'::text[])
           OR name LIKE ANY ('{%1%,%this%}'::text[])
ORDER  BY id
LIMIT  5;

Quoting the manual on window functions:

In addition to these functions, any built-in or user-defined aggregate function can be used as a window function

This works, because LIMIT is applied after window functions.

I also use an alternative syntax for array literals. One is as good as the other. This one is shorter for longer arrays. And sometimes an explicit type cast is needed. I am assuming text here.

It is simpler and a bit faster than the version with a CTE in my test.

BTW, this WHERE clause with a regular expression is shorter - but slower:

WHERE  description ~ '(1|this)'
           OR name ~ '(1|this)'

Ugly, but fast

One more test: I found the primitive version (similar to what you had already) to be even faster:

SELECT id, name, description
    , (SELECT count(*)
       FROM   products p
       WHERE  description LIKE ANY ('{%1%,%this%}'::text[])
                  OR name LIKE ANY ('{%1%,%this%}'::text[])
      ) AS total
FROM   products p
WHERE  description LIKE ANY ('{%1%,%this%}'::text[])
           OR name LIKE ANY ('{%1%,%this%}'::text[])
ORDER  BY id
LIMIT  5;



回答2:


Assuming you are using postgresql 9.0+, you can use CTE's for this. Eg.

WITH p AS (
        SELECT *
        FROM products
        WHERE description LIKE ANY (ARRAY['%1%','%this%']) OR name LIKE ANY (ARRAY['%1%','%this%'])
        )
SELECT  *,
        (select count(*) from p) as total
FROM p
ORDER BY id LIMIT 5;


来源:https://stackoverflow.com/questions/17734535/select-only-partial-result-but-get-total-number-of-rows

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!