복붙노트

[SQL] 어떻게 카테고리 별 최신 네 개의 항목을 선택하려면?

SQL

어떻게 카테고리 별 최신 네 개의 항목을 선택하려면?

나는 항목의 데이터베이스를 가지고있다. 각 항목은 범주 테이블의 범주 ID로 분류됩니다. 나는 페이지를 나열하는 모든 범주를 만들려고하고, 각 범주 아래에 그 카테고리에서 4 개 최신 항목을 표시합니다.

예를 들면 :

애완 동물 용품

img1
img2
img3
img4

애완 동물 사료

img1
img2
img3
img4

나는 쉽게과 같이 각 범주에 대한 데이터베이스를 조회하여이 문제를 해결할 수 있음을 알고있다 :

SELECT id FROM category

그런 다음 데이터를 반복하고 새로운 항목을 잡아 각 범주에 대한 데이터베이스를 조회 :

SELECT image FROM item where category_id = :category_id 
ORDER BY date_listed DESC LIMIT 4

난 그냥 한 쿼리를 사용하고 데이터를 모두 잡을 수 있다면 내가 알아 내려고 노력하고있어입니다. 나는 아마도 데이터베이스에 대한 호출의 수를 줄일 것이라고 생각, 그래서 나는 33 종류가 있습니다.

이 가능하면 누구나 알아? 33 호출하는 경우 큰 거래는 난 그냥 쉬운 방법을해야한다고하지 않습니다.

해결법

  1. ==============================

    1.이것은 가장 큰-N 당 그룹 문제이며, 그것은 매우 일반적인 SQL의 질문입니다.

    이것은 가장 큰-N 당 그룹 문제이며, 그것은 매우 일반적인 SQL의 질문입니다.

    여기에 외부 조인과 나는 그것을 해결하는 방법은 다음과 같습니다

    SELECT i1.*
    FROM item i1
    LEFT OUTER JOIN item i2
      ON (i1.category_id = i2.category_id AND i1.item_id < i2.item_id)
    GROUP BY i1.item_id
    HAVING COUNT(*) < 4
    ORDER BY category_id, date_listed;
    

    내가 항목 테이블의 기본 키를 가정하고있어 ITEM_ID이며, 그것은 일정하게 증가하는 pseudokey 있다고. 즉, ITEM_ID에서 큰 값이 항목에서 새로운 행에 대응한다.

    여기 그것이 작동하는 방법 : 각 항목에 대해, 새로운 다른 항목 중 일부가 있습니다. 예를 들어, 네 번째 최신 항목보다 새로운 세 가지 항목이 있습니다. 아주 최신 항목보다 새로운 제로 항목이 있습니다. 우리는 새로운이며, I1과 같은 범주를 항목 (I2)의 세트에 각 항목 (I1)를 비교하려는 그래서. 그 새로운 항목의 수가 4보다 작은 경우, I1은 우리가 포함 그 중 하나입니다. 그렇지 않으면, 포함되지 않습니다.

    이 솔루션의 장점은 상관없이 얼마나 많은 종류 작동 없다는 것입니다, 당신은 카테고리를 변경하는 경우 작업을 계속합니다. 또한 어떤 종류의 항목 수가 4보다 적은 경우에도 작동합니다.

    작동하지만 사용자 변수 기능은 MySQL에 의존하는 또 다른 방법 :

    SELECT *
    FROM (
        SELECT i.*, @r := IF(@g = category_id, @r+1, 1) AS rownum, @g := category_id
        FROM (@g:=null, @r:=0) AS _init
        CROSS JOIN item i
        ORDER BY i.category_id, i.date_listed
    ) AS t
    WHERE t.rownum <= 3;
    

    MySQL은 8.0.3 SQL 표준 윈도우 함수에 대한 지원을 소개했다. 이제 우리는 문제를 다른 RDBMS가하는 방식의이 종류를 해결할 수 있습니다 :

    WITH numbered_item AS (
      SELECT *, ROW_NUMBER() OVER (PARTITION BY category_id ORDER BY item_id) AS rownum
      FROM item
    )
    SELECT * FROM numbered_item WHERE rownum <= 4;
    
  2. ==============================

    2.이 솔루션은 다른 SO 솔루션에서 적응이 관련 / 비슷한 질문을 찾는 당신에게 RageZ 감사합니다.

    이 솔루션은 다른 SO 솔루션에서 적응이 관련 / 비슷한 질문을 찾는 당신에게 RageZ 감사합니다.

    노트

    이 솔루션은 저스틴의 사용 사례에 대한 만족스러운 것 같다. 사용 사례에 따라이 게시물에 빌 Karwin 또는 데이비드 안드레스 '솔루션을 확인 할 수 있습니다. 빌의 솔루션은 내 투표가있다! 나는 다음 서로 두 쿼리를 넣어, 왜 참조 ;-)

    내 솔루션의 장점은 CATEGORY_ID 당 하나 개의 레코드 (항목 테이블에서 정보를 원하시면 "롤업"입니다)을 반환합니다. 원하는 행의 수 증가에 따라 내 솔루션의 가장 큰 단점은 가독성의 부족과 그 복잡성이 증가이다 (오히려 6보다 카테고리 당 6 개 행이하는 말). 또한 항목 테이블의 행 수의 성장에 따라 약간 느려질 수 있습니다. (에 관계없이 모든 솔루션은 항목 테이블의 자격 행 적은 수의 더 나은 수행하고,이 중 하나를 주기적으로 삭제하거나 오래된 항목을 이동 및 / 또는 이른 행 아웃 도움말 SQL 필터에 플래그를 소개하는 것이 좋습니다)

    첫 번째 시도 (작동하지 않았다!) ...

    이 접근 방식의 문제점은 하위 쿼리가 [정당하지만 나쁜 우리가] 자기 조인에 의해 정의 된 직교 제품을 기반으로, 매우 많은 열을 생산하는 것이라고했다 ...

    SELECT id, CategoryName(?), tblFourImages.*
    FROM category
    JOIN (
        SELECT i1.category_id, i1.image as Image1, i2.image AS Image2, i3.image AS Image3, i4.image AS Image4
        FROM item AS i1
        LEFT JOIN item AS i2 ON i1.category_id = i2.category_id AND i1.date_listed > i2.date_listed
        LEFT JOIN item AS i3 ON i2.category_id = i3.category_id AND i2.date_listed > i3.date_listed
        LEFT JOIN item AS i4 ON i3.category_id = i4.category_id AND i3.date_listed > i4.date_listed
    ) AS tblFourImages ON tblFourImages.category_id = category.id
    --WHERE  here_some_addtional l criteria if needed
    ORDER BY id ASC;
    

    두 번째 시도. (작품을 좋아!)

    나열 된 날짜를 강제 부질 첨가에 절 될 WHERE 최근, 초 최신 thrird lateest 등 각각 (및 대 미만의 4 개 항목이 때 널 경우 허용 I1, I2, I3 등 주어진 카테고리 ID). 또한, 화상이없는 "판매"되는 항목 또는 항목을 도시 방지 무관 필터 절을 첨가 하였다 (추가 조건)

    이 논리는 (주어진 CATEGORY_ID에 대한) 중복 날짜 목록에 값이 없음을 가정한다. 이러한 경우가 그렇지 않은 경우 중복 행을 만들 것입니다. 효과적으로 나와있는 날짜의 사용은 단조 증가 기본 키의 빌의 솔루션에서 요구 / 정의 된 것입니다.

    SELECT id, CategoryName, tblFourImages.*
    FROM category
    JOIN (
        SELECT i1.category_id, i1.image as Image1, i2.image AS Image2, i3.image AS Image3, i4.image AS Image4, i4.date_listed
        FROM item AS i1
        LEFT JOIN item AS i2 ON i1.category_id = i2.category_id AND i1.date_listed > i2.date_listed AND i2.sold = FALSE AND i2.image IS NOT NULL
              AND i1.sold = FALSE AND i1.image IS NOT NULL
        LEFT JOIN item AS i3 ON i2.category_id = i3.category_id AND i2.date_listed > i3.date_listed AND i3.sold = FALSE AND i3.image IS NOT NULL
        LEFT JOIN item AS i4 ON i3.category_id = i4.category_id AND i3.date_listed > i4.date_listed AND i4.sold = FALSE AND i4.image IS NOT NULL
        WHERE NOT EXISTS (SELECT * FROM item WHERE category_id = i1.category_id AND date_listed > i1.date_listed)
          AND (i2.image IS NULL OR (NOT EXISTS (SELECT * FROM item WHERE category_id = i1.category_id AND date_listed > i2.date_listed AND date_listed <> i1.date_listed)))
          AND (i3.image IS NULL OR (NOT EXISTS (SELECT * FROM item WHERE category_id = i1.category_id AND date_listed > i3.date_listed AND date_listed <> i1.date_listed AND date_listed <> i2.date_listed)))
          AND (i4.image IS NULL OR (NOT EXISTS (SELECT * FROM item WHERE category_id = i1.category_id AND date_listed > i4.date_listed AND date_listed <> i1.date_listed AND date_listed <> i2.date_listed AND date_listed <> i3.date_listed)))
    ) AS tblFourImages ON tblFourImages.category_id = category.id
    --WHERE  --
    ORDER BY id ASC;
    

    이제 ... 내가 ITEM_ID 키를 소개하는 경우 다음을 비교하고 "외부"쿼리에이 목록을 제공하기 위해 빌의 솔루션을 사용합니다. 빌의 접근 방식이 더 나은 이유를 볼 수 있습니다 ...

    SELECT id, CategoryName, image, date_listed, item_id
    FROM item I
    LEFT OUTER JOIN category C ON C.id = I.category_id
    WHERE I.item_id IN 
    (
    SELECT i1.item_id
    FROM item i1
    LEFT OUTER JOIN item i2
      ON (i1.category_id = i2.category_id AND i1.item_id < i2.item_id
          AND i1.sold = 'N' AND i2.sold = 'N'
          AND i1.image <> '' AND i2.image <> ''
          )
    GROUP BY i1.item_id
    HAVING COUNT(*) < 4
    )
    ORDER BY category_id, item_id DESC
    
  3. ==============================

    3.다른 데이터베이스에서 당신은 ROW_NUMBER 함수를 사용하여이 작업을 수행 할 수 있습니다.

    다른 데이터베이스에서 당신은 ROW_NUMBER 함수를 사용하여이 작업을 수행 할 수 있습니다.

    SELECT
        category_id, image, date_listed
    FROM
    (
        SELECT
            category_id, image, date_listed,
            ROW_NUMBER() OVER (PARTITION BY category_id
                               ORDER BY date_listed DESC) AS rn
        FROM item
    ) AS T1
    WHERE rn <= 4
    

    불행하게도 MySQL은 ROW_NUMBER 기능을 지원하지 않습니다,하지만 당신은 변수를 사용하여 에뮬레이션 할 수 있습니다 :

    SELECT
        category_id, image, date_listed
    FROM
    (
        SELECT
            category_id, image, date_listed,
            @rn := IF(@prev = category_id, @rn + 1, 1) AS rn,
            @prev := category_id
        FROM item
        JOIN (SELECT @prev := NULL, @rn = 0) AS vars
        ORDER BY category_id, date_listed DESC
    ) AS T1
    WHERE rn <= 4
    

    온라인으로 작업을 참조하십시오 sqlfiddle를

    다음과 같이 작동합니다 :

  4. ==============================

    4.아니 아주 예쁜하지만 :

    아니 아주 예쁜하지만 :

    SELECT image 
    FROM item 
    WHERE date_listed IN (SELECT date_listed 
                          FROM item 
                          ORDER BY date_listed DESC LIMIT 4)
    
  5. ==============================

    5.당신의 범주는 어떻게 일정에 따라, 다음은 간단한 경로입니다

    당신의 범주는 어떻게 일정에 따라, 다음은 간단한 경로입니다

    SELECT C.CategoryName, R.Image, R.date_listed
    FROM
    (
        SELECT CategoryId, Image, date_listed
        FROM 
        (
          SELECT CategoryId, Image, date_listed
          FROM item
          WHERE Category = 'Pet Supplies'
          ORDER BY date_listed DESC LIMIT 4
        ) T
    
        UNION ALL
    
        SELECT CategoryId, Image, date_listed
        FROM
        (        
          SELECT CategoryId, Image, date_listed
          FROM item
          WHERE Category = 'Pet Food'
          ORDER BY date_listed DESC LIMIT 4
        ) T
    ) RecentItemImages R
    INNER JOIN Categories C ON C.CategoryId = R.CategoryId
    ORDER BY C.CategoryName, R.Image, R.date_listed
    
  6. ==============================

    6.쇼 아래 코드 루프에서 할 수있는 방법 확실히 편집을 많이 필요하지만 난이 도움이되기를 바랍니다.

    쇼 아래 코드 루프에서 할 수있는 방법 확실히 편집을 많이 필요하지만 난이 도움이되기를 바랍니다.

            declare @RowId int
     declare @CategoryId int
            declare @CategoryName varchar(MAX)
    
     create table PART (RowId int, CategoryId int, CategoryName varchar)
     create table  NEWESTFOUR(RowId int, CategoryId int, CategoryName varchar, Image image)
            select RowId = ROW_NUMBER(),CategoryId,CategoryName into PART from [Category Table]
    
    
            set @PartId = 0
     set @CategoryId = 0 
     while @Part_Id <= --count
     begin
       set @PartId = @PartId + 1
              SELECT @CategoryId = category_id, @CategoryName = category_name from PART where PartId = @Part_Id
              SELECT RowId = @PartId, image,CategoryId = @category_id, CategoryName = @category_name   FROM item into NEWESTFOUR where category_id = :category_id 
    ORDER BY date_listed DESC LIMIT 4
    
     end
     select * from NEWESTFOUR
     drop table NEWESTFOUR
            drop table PART
    
  7. ==============================

    7.최근에 나는 데이터베이스에 독립적 인 나를 위해 일한 쿼리를 시도, 비슷한 상황을 가로 질러왔다

    최근에 나는 데이터베이스에 독립적 인 나를 위해 일한 쿼리를 시도, 비슷한 상황을 가로 질러왔다

    SELECT i.* FROM Item AS i JOIN Category c ON i.category_id=c.id WHERE
    (SELECT count(*) FROM Item i1 WHERE 
    i1.category_id=i.category_id AND 
    i1.date_listed>=i.date_listed) <=3 
    ORDER BY category_id,date_listed DESC;
    

    이보다 새로운 항목이 적은 3보다 경우는 루프 2 실행하고 확인하는 것과 같습니다

  8. ==============================

    8.확인을 인터넷 검색 빠른 답변 후 것 그것은 MySQL을 적어도 수는 없습니다

    확인을 인터넷 검색 빠른 답변 후 것 그것은 MySQL을 적어도 수는 없습니다

    기준이 스레드

    어쩌면 서버를 아래로 떨어질 수 있도록하는 것을 두려워하는 경우 해당 쿼리의 결과를 캐시한다 당신은 코드를 더 잘 수행 할

  9. from https://stackoverflow.com/questions/1442527/how-to-select-the-newest-four-items-per-category by cc-by-sa and MIT license