> ## Documentation Index
> Fetch the complete documentation index at: https://private-7c7dfe99-fix-nav-issues.mintlify.site/llms.txt
> Use this file to discover all available pages before exploring further.

> 호텔, 레스토랑, 카페의 메뉴와 각 요리의 가격에 관한 과거 데이터 130만 건이 포함된 데이터셋입니다.

# New York Public Library "What's on the Menu?" 데이터셋

이 데이터셋은 New York Public Library에서 제작했습니다. 호텔, 레스토랑, 카페의 메뉴와 각 요리 가격에 관한 과거 데이터가 포함되어 있습니다.

출처: [http://menus.nypl.org/data](http://menus.nypl.org/data)
이 데이터는 퍼블릭 도메인에 속합니다.

이 데이터는 도서관 아카이브에서 가져온 것이므로 일부 내용이 누락되어 있을 수 있고, 통계 분석에 활용하기에는 어려움이 있을 수 있습니다. 그럼에도 꽤 먹음직스러운 데이터이기도 합니다.
메뉴에 포함된 요리 관련 데이터는 총 130만 건에 불과하므로 ClickHouse 기준으로는 매우 작은 규모이지만, 여전히 좋은 예시입니다.

<div id="download-dataset">
  ## 데이터셋 다운로드
</div>

다음 명령을 실행하십시오:

```bash theme={null}
wget https://s3.amazonaws.com/menusdata.nypl.org/gzips/2021_08_01_07_01_17_data.tgz
# 옵션: 체크섬 검증
md5sum 2021_08_01_07_01_17_data.tgz
# 체크섬 값: db6126724de939a5481e3160a2d67d15
```

필요한 경우 [http://menus.nypl.org/data에](http://menus.nypl.org/data에) 있는 최신 링크로 교체하십시오.
다운로드 용량은 약 35 MB입니다.

<div id="unpack-dataset">
  ## 데이터셋 압축 해제하기
</div>

```bash theme={null}
tar xvf 2021_08_01_07_01_17_data.tgz
```

압축되지 않은 크기는 약 150 MB입니다.

데이터는 정규화되어 있으며 4개의 테이블(table)로 구성됩니다:

* `Menu` — 메뉴에 대한 정보입니다. 식당 이름, 메뉴가 확인된 날짜 등이 포함됩니다.
* `Dish` — 요리에 대한 정보입니다. 요리 이름과 몇 가지 특성이 포함됩니다.
* `MenuPage` — 메뉴의 페이지에 대한 정보입니다. 각 페이지는 하나의 메뉴에 속합니다.
* `MenuItem` — 메뉴 항목입니다. 특정 메뉴 페이지에 있는 요리와 해당 가격을 나타내며, 요리와 메뉴 페이지를 연결합니다.

<div id="create-tables">
  ## 테이블 만들기
</div>

가격을 저장할 때 [Decimal](/ko/reference/data-types/decimal) 데이터 타입을 사용합니다.

```sql theme={null}
CREATE TABLE dish
(
    id UInt32,
    name String,
    description String,
    menus_appeared UInt32,
    times_appeared Int32,
    first_appeared UInt16,
    last_appeared UInt16,
    lowest_price Decimal64(3),
    highest_price Decimal64(3)
) ENGINE = MergeTree ORDER BY id;

CREATE TABLE menu
(
    id UInt32,
    name String,
    sponsor String,
    event String,
    venue String,
    place String,
    physical_description String,
    occasion String,
    notes String,
    call_number String,
    keywords String,
    language String,
    date String,
    location String,
    location_type String,
    currency String,
    currency_symbol String,
    status String,
    page_count UInt16,
    dish_count UInt16
) ENGINE = MergeTree ORDER BY id;

CREATE TABLE menu_page
(
    id UInt32,
    menu_id UInt32,
    page_number UInt16,
    image_id String,
    full_height UInt16,
    full_width UInt16,
    uuid UUID
) ENGINE = MergeTree ORDER BY id;

CREATE TABLE menu_item
(
    id UInt32,
    menu_page_id UInt32,
    price Decimal64(3),
    high_price Decimal64(3),
    dish_id UInt32,
    created_at DateTime,
    updated_at DateTime,
    xpos Float64,
    ypos Float64
) ENGINE = MergeTree ORDER BY id;
```

<div id="import-data">
  ## 데이터 가져오기
</div>

ClickHouse에 데이터를 업로드하려면 다음 명령을 실행하십시오:

```bash theme={null}
clickhouse-client --format_csv_allow_single_quotes 0 --input_format_null_as_default 0 --query "INSERT INTO dish FORMAT CSVWithNames" < Dish.csv
clickhouse-client --format_csv_allow_single_quotes 0 --input_format_null_as_default 0 --query "INSERT INTO menu FORMAT CSVWithNames" < Menu.csv
clickhouse-client --format_csv_allow_single_quotes 0 --input_format_null_as_default 0 --query "INSERT INTO menu_page FORMAT CSVWithNames" < MenuPage.csv
clickhouse-client --format_csv_allow_single_quotes 0 --input_format_null_as_default 0 --date_time_input_format best_effort --query "INSERT INTO menu_item FORMAT CSVWithNames" < MenuItem.csv
```

데이터가 헤더가 있는 CSV로 표현되므로 [CSVWithNames](/ko/reference/formats/CSV/CSVWithNames) 포맷을 사용합니다.

데이터 필드에는 큰따옴표만 사용하고 작은따옴표는 값 내부에 포함될 수 있으므로, CSV 파서가 이를 혼동하지 않도록 `format_csv_allow_single_quotes`를 비활성화합니다.

데이터에 [NULL](/ko/reference/settings/formats#input_format_null_as_default)이 없으므로 [input\_format\_null\_as\_default](/ko/reference/settings/formats#input_format_null_as_default)를 비활성화합니다. 그렇지 않으면 ClickHouse가 `\N` 시퀀스를 파싱하려고 시도하여 데이터 내의 `\`와 혼동할 수 있습니다.

설정 [date\_time\_input\_format best\_effort](/ko/reference/settings/formats#date_time_input_format)를 사용하면 [DateTime](/ko/reference/data-types/datetime) 필드를 매우 다양한 포맷으로 파싱할 수 있습니다. 예를 들어 초가 없는 ISO-8601 형식인 '2000-01-01 01:02'도 인식됩니다. 이 설정이 없으면 고정된 DateTime 포맷만 허용됩니다.

<div id="denormalize-data">
  ## 데이터 비정규화
</div>

데이터는 [정규화된 형태](https://en.wikipedia.org/wiki/Database_normalization#Normal_forms)로 여러 테이블에 나뉘어 있습니다. 즉, 예를 들어 메뉴 항목에서 요리 이름을 조회하려면 [JOIN](/ko/reference/statements/select/join)을 수행해야 합니다.
일반적인 분석 작업에서는 매번 `JOIN`을 수행하지 않기 위해 사전에 JOIN된 데이터를 사용하는 편이 훨씬 더 효율적입니다. 이를 "비정규화된" 데이터라고 합니다.

이제 모든 데이터를 JOIN해 함께 담은 `menu_item_denorm` 테이블을 생성하겠습니다:

```sql theme={null}
CREATE TABLE menu_item_denorm
ENGINE = MergeTree ORDER BY (dish_name, created_at)
AS SELECT
    price,
    high_price,
    created_at,
    updated_at,
    xpos,
    ypos,
    dish.id AS dish_id,
    dish.name AS dish_name,
    dish.description AS dish_description,
    dish.menus_appeared AS dish_menus_appeared,
    dish.times_appeared AS dish_times_appeared,
    dish.first_appeared AS dish_first_appeared,
    dish.last_appeared AS dish_last_appeared,
    dish.lowest_price AS dish_lowest_price,
    dish.highest_price AS dish_highest_price,
    menu.id AS menu_id,
    menu.name AS menu_name,
    menu.sponsor AS menu_sponsor,
    menu.event AS menu_event,
    menu.venue AS menu_venue,
    menu.place AS menu_place,
    menu.physical_description AS menu_physical_description,
    menu.occasion AS menu_occasion,
    menu.notes AS menu_notes,
    menu.call_number AS menu_call_number,
    menu.keywords AS menu_keywords,
    menu.language AS menu_language,
    menu.date AS menu_date,
    menu.location AS menu_location,
    menu.location_type AS menu_location_type,
    menu.currency AS menu_currency,
    menu.currency_symbol AS menu_currency_symbol,
    menu.status AS menu_status,
    menu.page_count AS menu_page_count,
    menu.dish_count AS menu_dish_count
FROM menu_item
    JOIN dish ON menu_item.dish_id = dish.id
    JOIN menu_page ON menu_item.menu_page_id = menu_page.id
    JOIN menu ON menu_page.menu_id = menu.id;
```

<div id="validate-data">
  ## 데이터 확인
</div>

```sql title="Query" theme={null}
SELECT count() FROM menu_item_denorm;
```

```text title="Response" theme={null}
┌─count()─┐
│ 1329175 │
└─────────┘
```

<div id="run-queries">
  ## 몇 가지 쿼리 실행해 보기
</div>

<div id="query-averaged-historical-prices">
  ### 요리의 과거 평균 가격
</div>

```sql title="Query" theme={null}
SELECT
    round(toUInt32OrZero(extract(menu_date, '^\\d{4}')), -1) AS d,
    count(),
    round(avg(price), 2),
    bar(avg(price), 0, 100, 100)
FROM menu_item_denorm
WHERE (menu_currency = 'Dollars') AND (d > 0) AND (d < 2022)
GROUP BY d
ORDER BY d ASC;
```

```text title="Response" theme={null}
┌────d─┬─count()─┬─round(avg(price), 2)─┬─bar(avg(price), 0, 100, 100)─┐
│ 1850 │     618 │                  1.5 │ █▍                           │
│ 1860 │    1634 │                 1.29 │ █▎                           │
│ 1870 │    2215 │                 1.36 │ █▎                           │
│ 1880 │    3909 │                 1.01 │ █                            │
│ 1890 │    8837 │                  1.4 │ █▍                           │
│ 1900 │  176292 │                 0.68 │ ▋                            │
│ 1910 │  212196 │                 0.88 │ ▊                            │
│ 1920 │  179590 │                 0.74 │ ▋                            │
│ 1930 │   73707 │                  0.6 │ ▌                            │
│ 1940 │   58795 │                 0.57 │ ▌                            │
│ 1950 │   41407 │                 0.95 │ ▊                            │
│ 1960 │   51179 │                 1.32 │ █▎                           │
│ 1970 │   12914 │                 1.86 │ █▋                           │
│ 1980 │    7268 │                 4.35 │ ████▎                        │
│ 1990 │   11055 │                 6.03 │ ██████                       │
│ 2000 │    2467 │                11.85 │ ███████████▋                 │
│ 2010 │     597 │                25.66 │ █████████████████████████▋   │
└──────┴─────────┴──────────────────────┴──────────────────────────────┘
```

너무 곧이곧대로 받아들이지는 마십시오.

<div id="query-burger-prices">
  ### 버거 가격
</div>

```sql title="Query" theme={null}
SELECT
    round(toUInt32OrZero(extract(menu_date, '^\\d{4}')), -1) AS d,
    count(),
    round(avg(price), 2),
    bar(avg(price), 0, 50, 100)
FROM menu_item_denorm
WHERE (menu_currency = 'Dollars') AND (d > 0) AND (d < 2022) AND (dish_name ILIKE '%burger%')
GROUP BY d
ORDER BY d ASC;
```

```text title="Response" theme={null}
┌────d─┬─count()─┬─round(avg(price), 2)─┬─bar(avg(price), 0, 50, 100)───────────┐
│ 1880 │       2 │                 0.42 │ ▋                                     │
│ 1890 │       7 │                 0.85 │ █▋                                    │
│ 1900 │     399 │                 0.49 │ ▊                                     │
│ 1910 │     589 │                 0.68 │ █▎                                    │
│ 1920 │     280 │                 0.56 │ █                                     │
│ 1930 │      74 │                 0.42 │ ▋                                     │
│ 1940 │     119 │                 0.59 │ █▏                                    │
│ 1950 │     134 │                 1.09 │ ██▏                                   │
│ 1960 │     272 │                 0.92 │ █▋                                    │
│ 1970 │     108 │                 1.18 │ ██▎                                   │
│ 1980 │      88 │                 2.82 │ █████▋                                │
│ 1990 │     184 │                 3.68 │ ███████▎                              │
│ 2000 │      21 │                 7.14 │ ██████████████▎                       │
│ 2010 │       6 │                18.42 │ ████████████████████████████████████▋ │
└──────┴─────────┴──────────────────────┴───────────────────────────────────────┘
```

<div id="query-vodka">
  ### 보드카
</div>

```sql title="Query" theme={null}
SELECT
    round(toUInt32OrZero(extract(menu_date, '^\\d{4}')), -1) AS d,
    count(),
    round(avg(price), 2),
    bar(avg(price), 0, 50, 100)
FROM menu_item_denorm
WHERE (menu_currency IN ('Dollars', '')) AND (d > 0) AND (d < 2022) AND (dish_name ILIKE '%vodka%')
GROUP BY d
ORDER BY d ASC;
```

```text title="Response" theme={null}
┌────d─┬─count()─┬─round(avg(price), 2)─┬─bar(avg(price), 0, 50, 100)─┐
│ 1910 │       2 │                    0 │                             │
│ 1920 │       1 │                  0.3 │ ▌                           │
│ 1940 │      21 │                 0.42 │ ▋                           │
│ 1950 │      14 │                 0.59 │ █▏                          │
│ 1960 │     113 │                 2.17 │ ████▎                       │
│ 1970 │      37 │                 0.68 │ █▎                          │
│ 1980 │      19 │                 2.55 │ █████                       │
│ 1990 │      86 │                  3.6 │ ███████▏                    │
│ 2000 │       2 │                 3.98 │ ███████▊                    │
└──────┴─────────┴──────────────────────┴─────────────────────────────┘
```

보드카를 찾으려면 `ILIKE '%vodka%'`라고 써야 하며, 이건 확실히 강한 인상을 줍니다.

<div id="query-caviar">
  ### 캐비아
</div>

캐비아 가격을 출력해 보겠습니다. 또한 캐비아가 들어간 아무 요리나 하나의 이름도 출력해 보겠습니다.

```sql title="Query" theme={null}
SELECT
    round(toUInt32OrZero(extract(menu_date, '^\\d{4}')), -1) AS d,
    count(),
    round(avg(price), 2),
    bar(avg(price), 0, 50, 100),
    any(dish_name)
FROM menu_item_denorm
WHERE (menu_currency IN ('Dollars', '')) AND (d > 0) AND (d < 2022) AND (dish_name ILIKE '%caviar%')
GROUP BY d
ORDER BY d ASC;
```

```text title="Response" theme={null}
┌────d─┬─count()─┬─round(avg(price), 2)─┬─bar(avg(price), 0, 50, 100)──────┬─any(dish_name)──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┐
│ 1090 │       1 │                    0 │                                  │ Caviar                                                                                                                              │
│ 1880 │       3 │                    0 │                                  │ Caviar                                                                                                                              │
│ 1890 │      39 │                 0.59 │ █▏                               │ Butter and caviar                                                                                                                   │
│ 1900 │    1014 │                 0.34 │ ▋                                │ Anchovy Caviar on Toast                                                                                                             │
│ 1910 │    1588 │                 1.35 │ ██▋                              │ 1/1 Brötchen Caviar                                                                                                                 │
│ 1920 │     927 │                 1.37 │ ██▋                              │ ASTRAKAN CAVIAR                                                                                                                     │
│ 1930 │     289 │                 1.91 │ ███▋                             │ Astrachan caviar                                                                                                                    │
│ 1940 │     201 │                 0.83 │ █▋                               │ (SPECIAL) Domestic Caviar Sandwich                                                                                                  │
│ 1950 │      81 │                 2.27 │ ████▌                            │ Beluga Caviar                                                                                                                       │
│ 1960 │     126 │                 2.21 │ ████▍                            │ Beluga Caviar                                                                                                                       │
│ 1970 │     105 │                 0.95 │ █▊                               │ BELUGA MALOSSOL CAVIAR AMERICAN DRESSING                                                                                            │
│ 1980 │      12 │                 7.22 │ ██████████████▍                  │ Authentic Iranian Beluga Caviar the world's finest black caviar presented in ice garni and a sampling of chilled 100° Russian vodka │
│ 1990 │      74 │                14.42 │ ████████████████████████████▋    │ Avocado Salad, Fresh cut avocado with caviare                                                                                       │
│ 2000 │       3 │                 7.82 │ ███████████████▋                 │ Aufgeschlagenes Kartoffelsueppchen mit Forellencaviar                                                                               │
│ 2010 │       6 │                15.58 │ ███████████████████████████████▏ │ "OYSTERS AND PEARLS" "Sabayon" of Pearl Tapioca with Island Creek Oysters and Russian Sevruga Caviar                                │
└──────┴─────────┴──────────────────────┴──────────────────────────────────┴─────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┘
```

적어도 보드카와 함께 즐길 캐비아는 있습니다. 아주 좋습니다.

<div id="playground">
  ## 온라인 플레이그라운드
</div>

데이터가 ClickHouse Playground에 업로드되어 있습니다. [예시](https://sql.clickhouse.com?query_id=KB5KQJJFNBKHE5GBUJCP1B).
