ORM: Sequelize: MySQL쿼리를 Sequelize 쿼리로 바꾸고싶습니다.

조회수 1198회

우선 데이터와 테이블을 아래와 같습니다.

CREATE TABLE products
(
    p_id INT,
    p_name VARCHAR(32),
    PRIMARY KEY (p_id)
);

CREATE TABLE color
(
    p_id INT,
    p_color VARCHAR(24)
);

CREATE TABLE size
(
    p_id INT,
    p_size VARCHAR(12)
);

INSERT INTO products
VALUES
(1, 'test1'),
(2, 'test2'),
(3, 'test3'),
(4, 'test4'),
(5, 'test5');

INSERT INTO color
VALUES
(1, 'black'),
(1, 'white'),
(1, 'gray'),
(2, 'black'),
(2, 'beige'),
(3, 'blue'),
(3, 'red'),
(3, 'green'),
(4, 'black'),
(4, 'white'),
(4, 'beige'),
(4, 'gray'),
(4, 'mustard'),
(5, 'black'),
(5, 'white');

INSERT INTO size
VALUES
(1, 'S'),
(1, 'M'),
(1, 'L'),
(1, 'XL'),
(2, 'Free'),
(3, 'XS'),
(3, 'S'),
(3, 'M'),
(3, 'L'),
(3, 'XL'),
(4, 'M'),
(4, 'L'),
(4, 'XL'),
(5, 'Free');

sql문으로 콘솔에서 이렇게 넣는 쿼리를 sequelize에서 넣는 쿼리로 바꾸고싶습니다.

SELECT p.p_Id, p.p_name, s.p_size, c.p_color 
FROM products AS p 
LEFT OUTER JOIN size AS s 
ON p.p_id = s.p_id
LEFT OUTER JOIN color AS c
ON p.p_id = c.p_id;

+-----------+-------------+-------------+--------------+
| productId | productName | productSize | productColor |
+-----------+-------------+-------------+--------------+
|         1 | test_1      | S           | black        |
|         1 | test_1      | M           | black        |
|         1 | test_1      | L           | black        |
|         1 | test_1      | XL          | black        |
|         1 | test_1      | S           | white        |
|         1 | test_1      | M           | white        |
|         1 | test_1      | L           | white        |
|         1 | test_1      | XL          | white        |
|         1 | test_1      | S           | gray         |
|         1 | test_1      | M           | gray         |
|         1 | test_1      | L           | gray         |
|         1 | test_1      | XL          | gray         |
|         2 | test_2      | Free        | black        |
|         2 | test_2      | Free        | beige        |
|         3 | test_3      | XS          | blue         |
|         3 | test_3      | S           | blue         |
|         3 | test_3      | M           | blue         |
|         3 | test_3      | L           | blue         |
|         3 | test_3      | XL          | blue         |
|         3 | test_3      | XS          | red          |
|         3 | test_3      | S           | red          |
|         3 | test_3      | M           | red          |
|         3 | test_3      | L           | red          |
|         3 | test_3      | XL          | red          |
|         3 | test_3      | XS          | green        |
|         3 | test_3      | S           | green        |
|         3 | test_3      | M           | green        |
|         3 | test_3      | L           | green        |
|         3 | test_3      | XL          | green        |
|         4 | test_4      | M           | black        |
|         4 | test_4      | L           | black        |
|         4 | test_4      | XL          | black        |
|         4 | test_4      | M           | white        |
|         4 | test_4      | L           | white        |
|         4 | test_4      | XL          | white        |
|         4 | test_4      | M           | beige        |
|         4 | test_4      | L           | beige        |
|         4 | test_4      | XL          | beige        |
|         4 | test_4      | M           | gray         |
|         4 | test_4      | L           | gray         |
|         4 | test_4      | XL          | gray         |
|         4 | test_4      | M           | mustard      |
|         4 | test_4      | L           | mustard      |
|         4 | test_4      | XL          | mustard      |
|         5 | test_5      | Free        | black        |
|         5 | test_5      | Free        | white        |
+-----------+-------------+-------------+--------------+
46 rows in set (0.00 sec)
models.products.findAll({
        include: [{
            model: models.size,
            required: false,
        }, {
            model: models.color,
            required: false
        }]
    });

물론 색상, 사이즈 테이블에서는 p_id가 여러 개이기 때문에 PK설정을 할 수가 없어 위와 같이 쿼리를 실행하니까 products테이블에서만 제약을 설정했습니다. 그리고 p_id가 테이블마다 공통으로 오는 컬럼이기 때문에 FKp_id로만 설정을 해뒀구요.

SELECT 
`p`.`p_id`, 
`p`.`p_name`, 

`s`.`p_id` AS `s.p_id`, 
`s`.`p_id` AS `s.p_id`, 
`s`.`p_size` AS `s.p_size`, 

`c`.`p_id` AS `c.p_id`, 
`c`.`p_id` AS `c.p_id`, 
`c`.`p_color` AS `c.p_color`, 

FROM `products` AS `p` 

LEFT OUTER JOIN `size` AS `s` 
ON `p`.`p_id` = `s`.`id`

LEFT OUTER JOIN `color` AS `c` 
ON `p`.`p_id` = `c`.`id`

같이 나와버립니다. 제가 의도한 바는 ON절에서 p.p_id = c.p_id가 되어야 하는데 색상 테이블에는 있지도 않은 컬럼이 와서 p.p_id = c.id로 떠버립니다. ON절을 sequelize 수준에서 생쿼리를 쓰지 않고 해결하는 방법을 알고 계시는 분께 도움을 요청하는 바입니다.

  • (•́ ✖ •̀)
    알 수 없는 사용자
  • 좀더 찾아봤는데 최종 db.js에서 hasMany() 같은걸 정의하라는데요. 이걸 해놓으면 알아서 잡아준다고. https://lorenstewart.me/2016/09/12/sequelize-table-associations-joins/#1declaringassociationsinaconfigfile 이부분은 어떤가요? 애초에 size, color 테이블에 PK가 없는데... PK를 못잡는 상황이라면 include 배열안에 where 넣어서 잡으셔야 할것 같은데요. 엽토군 2019.1.23 10:25
  • 엽토님 답변 감사히 잘읽었습니다. 다른 문제가 생겼습니다. 더 깊은 문제가 생겼습니다 흑흑. 아래에 추가 내용을 작성했습니다. 읽어주시면 정말 감사하겠습니다. 알 수 없는 사용자 2019.1.23 11:16

1 답변

  • 질문 드리고 나서 association도 잡아봤고요. 그리고 sequelize-cli 고질적인 문제가 id, updatedAt, createdAtmigrations에 자동으로 생성되서 생기는 문제도 관련 메소드로 해결했습니다. 코드는 아래와 같습니다.

    products.hasMany(db.size, {
        foreignKey: 'p_id'
    });
    products.hasMany(db.color, {
        foreignKey: 'p_id'
    });
    color.belongsTo(db.products, {
        foreignKey: 'p_id'
    });
    size.belongsTo(db.products, {
        foreignKey: 'p_id'
    });
    
    db.ProductColor.removeAttribute('id');
    db.ProductSize.removeAttribute('id');
    

    웃기는게 belongsTo()를 없애고 hasMany()로만 관계설정을 해줘도 JSON 데이터가 반환되더라구요.

    let productId = req.params.id;
    const product = products.findAll({
            where: {
                p_id: productId
            },
            include: [{
                model: size,
                attributes: {
                    exclude: ['p_id', 'id', 'createdAt', 'updatedAt']
                }
            }, {
                model: color,
                attributes: {
                    exclude: ['p_id', 'id', 'createdAt', 'updatedAt']
                }
            }]
        });
    

    이렇게 쿼리를 작성했습니다. p_id는 products 테이블과 중복돼서 제거했고, createdAt, updatedAt은 테이블에 존재하지 않는 데이터라 제거했습니다. 마지막으로 id는 아까 removeAttribute()를 사용해서 따로 제외하지 않아도 딱히 등장하지는 않더라구요. 반환된 JSON 데이터는 다음과 같습니다.

    {
      "product": [
        {
          "p_id": 1,
          "p_ame": "test_1",
          "createdAt": "2018-10-13T00:00:00.000Z",
          "updatedAt": "2018-10-14T00:00:00.000Z",
          "size": [
            {
              "size": "S"
            }
          ],
          "color": [
            {
              "color": "black"
            }
          ]
        }
      ]
    }
    

    이렇게 나옵니다. size 테이블에 p_id가 1인 레코드들은 4개고 color 테이블에는 3개인데 위에서는 1개씩밖에 안나오니 너무 답답하네요.

    "size": [
    {
        "productSize": "S"
    },
    {
        "productSize": "M"
    },
    {
        "productSize": "L"
    },
    {
        "productSize": "XL"
    }
    ],
    "color": [
    {
        "productColor": "black"
    },
    {
        "productColor": "white"
    },
    {
        "productColor": "gray"
    }
    ]
    

    이 경우가 다대다는 아닌거같아서 연결테이블은 만들지 않았습니다. 도움이 절실합니다. 뭐가 문제인지를 모르겠네요 ㅠㅠ

    • (•́ ✖ •̀)
      알 수 없는 사용자
    • left outer join 관련해서 찾아보니 include 의 required 가 true 냐 false 냐에 따라 결과가 다른가 본데요. 잘 모르면서 아무거나 막 찾아와 혼란만 드리고 있는 건 아닌지 모르겠지만... https://blog.kozakana.net/2018/04/sequelize_join/ 엽토군 2019.1.23 23:15
    • 엽토군님 답변 달아주셔 감사합니다^^ required옵션은 작성안하면 자동으로 false가 돼서 LEFT OUTER JOIN이 되고 true로 옵션을 주면 INNER JOIN이 됩니다. 그래서 required를 따로 작성해주지 않았습니다. raw: true옵션으로 어찌저찌 해결했습니다만 데이터 모양이 이쁘게 나오진 못하더라구요. https://github.com/sequelize/sequelize/issues/5193#issuecomment-386153316 링크 타고 들어가셔서 한번 봐두시면 좋을 것같습니다. 절 도와주는 분이 계신다는데 크게 감사하고 있습니다. 다시한번 답글 달아주셔서 감사합니다 :) 알 수 없는 사용자 2019.1.24 11:47

답변을 하려면 로그인이 필요합니다.

프로그래머스 커뮤니티는 개발자들을 위한 Q&A 서비스입니다. 로그인해야 답변을 작성하실 수 있습니다.

(ಠ_ಠ)
(ಠ‿ಠ)