문제

카테고리에 대한 FK가 포함 된 제품 테이블이 있는데, 카테고리 테이블은 각 카테고리가 상위 카테고리를 가질 수있는 방식으로 작성됩니다.

Computers
    Processors
        Intel
            Pentium
            Core 2 Duo
        AMD
            Athlon

선택한 카테고리가 프로세서 인 경우 Intel, Pentium, Core 2 Duo, AMD 등에있는 제품을 반환 할 수 있다는 선택한 쿼리를 만들어야합니다.

DB의 모든 범주에 대한 모든 범주를 계층 구조에 저장하고 WHERE 절에 "in"을 포함시키는 일종의 "캐시"를 작성하는 것에 대해 생각했습니다. 이것이 최선의 솔루션입니까?

도움이 되었습니까?

해결책

이를위한 최상의 솔루션은 데이터베이스 디자인 단계에 있습니다. 카테고리 테이블은 a입니다 중첩 된 세트. 기사 MySQL에서 계층 적 데이터 관리 MySQL이 제목에도 불구하고 데이터베이스 테이블에 계층 구조를 저장하는 다양한 방법에 대한 훌륭한 개요를 제공합니다.

경영진 요약 :

중첩 된 세트

  • 어떤 깊이에 대해서는 선택이 쉽습니다
  • 삽입 및 삭제는 어렵습니다

표준 parent_id 기반 계층

  • Select는 내부 조인을 기준으로합니다 (따라서 빠르게 털이 많음)
  • 삽입 및 삭제가 쉽습니다

따라서 예제를 기준으로, 계층 구조 테이블이 중첩 된 세트 인 경우 쿼리가 다음과 같이 보입니다.

SELECT * FROM products 
   INNER JOIN categories ON categories.id = products.category_id 
WHERE categories.lft > 2 and categories.rgt < 11

2와 11은 각각 왼쪽과 오른쪽입니다. Processors 기록.

다른 팁

공통 테이블 표현식의 작업처럼 보입니다. 선을 따라 무언가 :

with catCTE (catid, parentid)
as
(
select cat.catid, cat.catparentid from cat where cat.name = 'Processors'
UNION ALL
select cat.catid, cat.catparentid from cat inner join catCTE on cat.catparentid=catcte.catid
)
select distinct * from catCTE

이는 이름이 '프로세서'인 범주와 그 자손의 범주를 선택해야합니다.

나는 과거에 비슷한 일을했고, 카테고리 ID에 대한 먼저 쿼리를 한 다음 해당 카테고리에서 제품에 대한 쿼리를했습니다. 카테고리를 얻는 것은 어려운 일이며 몇 가지 옵션이 있습니다.

  • 카테고리의 중첩 수준이 알려 지거나 상한을 찾을 수있는 경우 : 많은 결합으로 끔찍한 선택을 구축하십시오. 이것은 빠르지 만 추악하며 계층의 레벨에 제한을 설정해야합니다.
  • 비교적 적은 수의 총 카테고리가있는 경우, 모든 카테고리를 쿼리하고 (ID, 부모)를 쿼리하고 관심있는 ID의 ID를 수집하고 제품에 대한 선택을 수행하십시오. 이것은 나에게 적절한 옵션이었습니다.
  • 일련의 선택을 사용하여 계층 구조를 위/아래로 쿼리하십시오. 단순하지만 비교적 느립니다.
  • 최근 버전의 SQLServer는 재귀 쿼리를 지원하지만 직접 사용하지는 않았습니다.

저장된 절차는이 앱 측을 원하지 않으면 도움이 될 수 있습니다.

당신이 찾고 싶은 것은 "부모"관계 범주의 전이 폐쇄입니다. 카테고리 계층 깊이에 제한이 없다고 가정하므로 모든 범주를 찾는 단일 SQL 쿼리를 공식화 할 수 없습니다. 내가 할 수있는 일 (의사 코드)은 다음과 같습니다.

categoriesSet = empty set
while new.size > 0:
  new = select * from categories where parent in categoriesSet
  categoriesSet = categoriesSet+new

따라서 더 이상 발견되지 않을 때까지 어린이를위한 쿼리를 계속하십시오. 이는 퇴보 된 계층 구조 (예 : 1000 카테고리, 각각의 자녀) 또는 많은 총 범주가없는 한 속도 측면에서 잘 작동합니다. 두 번째 경우에는 항상 임시 테이블로 작업하여 앱과 데이터베이스 간의 데이터 전송을 작은 것으로 유지할 수 있습니다.

아마도 :

select *
from products
where products.category_id IN
  (select c2.category_id 
   from categories c1 inner join categories c2 on c1.category_id = c2.parent_id
   where c1.category = 'Processors'
   group by c2.category_id)

편집] 범주 깊이가 하나보다 큰 경우 가장 안쪽 쿼리가 형성됩니다. 내부 쿼리에 의해 반환 된 ID가 어린이가 없을 때까지 테이블에서 드릴 다운하는 저장된 절차를 설계 할 수 있다고 생각합니다. 해당 ID에서 외부 쿼리를 수행하십시오.

CREATE TABLE #categories (id INT NOT NULL, parentId INT, [name] NVARCHAR(100))
INSERT INTO #categories
    SELECT 1, NULL, 'Computers'
    UNION
SELECT 2, 1, 'Processors'
    UNION
SELECT 3, 2, 'Intel'
    UNION
SELECT 4, 2, 'AMD'
    UNION
SELECT 5, 3, 'Pentium'
    UNION
SELECT 6, 3, 'Core 2 Duo'
    UNION
SELECT 7, 4, 'Athlon'
SELECT * 
    FROM #categories
DECLARE @id INT
    SET @id = 2
            ; WITH r(id, parentid, [name]) AS (
    SELECT id, parentid, [name] 
        FROM #categories c 
        WHERE id = @id
        UNION ALL
    SELECT c.id, c.parentid, c.[name] 
        FROM #categories c  JOIN r ON c.parentid=r.id
    )
SELECT * 
    FROM products 
    WHERE p.productd IN
(SELECT id 
    FROM r)
DROP TABLE #categories   

예제의 마지막 부분은 이렇게 똑바로 실행하는 경우 실제로 작동하지 않습니다. 제품에서 선택을 제거하고 간단한 선택 *으로 대체하십시오.

이것은 주어진 카타고리에서 시작하는 모든 '자녀'카타이어리를 되풀이해야합니다.

DECLARE @startingCatagoryId int
DECLARE @current int
SET @startingCatagoryId = 13813 -- or whatever the CatagoryId is for 'Processors'

CREATE TABLE #CatagoriesToFindChildrenFor
(CatagoryId int)

CREATE TABLE #CatagoryTree
(CatagoryId int)

INSERT INTO #CatagoriesToFindChildrenFor VALUES (@startingCatagoryId)

WHILE (SELECT count(*) FROM #CatagoriesToFindChildrenFor) > 0
BEGIN
    SET @current = (SELECT TOP 1 * FROM #CatagoriesToFindChildrenFor)

    INSERT INTO #CatagoriesToFindChildrenFor
    SELECT ID FROM Catagory WHERE ParentCatagoryId = @current AND Deleted = 0

    INSERT INTO #CatagoryTree VALUES (@current)
    DELETE #CatagoriesToFindChildrenFor WHERE CatagoryId = @current
END

SELECT * FROM #CatagoryTree ORDER BY CatagoryId

DROP TABLE #CatagoriesToFindChildrenFor
DROP TABLE #CatagoryTree

계층 구조 데이터에 스택 온도 테이블을 사용하고 싶습니다. 다음은 거친 예입니다.

-- create a categories table and fill it with 10 rows (with random parentIds)
CREATE TABLE Categories ( Id uniqueidentifier, ParentId uniqueidentifier )
GO

INSERT
INTO   Categories
SELECT NEWID(),
       NULL 
GO

INSERT
INTO   Categories
SELECT   TOP(1)NEWID(),
         Id
FROM     Categories
ORDER BY Id
GO 9


DECLARE  @lvl INT,            -- holds onto the level as we move throught the hierarchy
         @Id Uniqueidentifier -- the id of the current item in the stack

SET @lvl = 1

CREATE TABLE #stack (item UNIQUEIDENTIFIER, [lvl] INT)
-- we fill fill this table with the ids we want
CREATE TABLE #tmpCategories (Id UNIQUEIDENTIFIER)

-- for this example we’ll just select all the ids 
-- if we want all the children of a specific parent we would include it’s id in
-- this where clause
INSERT INTO #stack SELECT Id, @lvl FROM Categories WHERE ParentId IS NULL

WHILE @lvl > 0
BEGIN -- begin 1

      IF EXISTS ( SELECT * FROM #stack WHERE lvl = @lvl )
      BEGIN -- begin 2

      SELECT @Id = [item]
      FROM #stack
      WHERE lvl = @lvl

      INSERT INTO #tmpCategories
      SELECT @Id

      DELETE FROM #stack
      WHERE lvl = @lvl
      AND item = @Id

      INSERT INTO #stack
      SELECT Id, @lvl + 1
      FROM   Categories
      WHERE  ParentId = @Id

      IF @@ROWCOUNT > 0
      BEGIN -- begin 3
         SELECT @lvl = @lvl + 1
      END -- end 3
   END -- end 2
   ELSE
   SELECT @lvl = @lvl - 1

END -- end 1

DROP TABLE #stack

SELECT * FROM #tmpCategories
DROP TABLE #tmpCategories
DROP TABLE Categories

여기에는 좋은 설명이 있습니다 링크 텍스트

며칠 전에 또 다른 질문에 대한 나의 대답은 여기에 적용됩니다 ... SQL의 재귀

내가 연결 한 책에는 당신의 상황을 멋지게 다루어야 할 몇 가지 방법이 있습니다.

라이센스 : CC-BY-SA ~와 함께 속성
제휴하지 않습니다 StackOverflow
scroll top