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

# Партиции таблицы

> Что такое партиции таблицы в ClickHouse

export const RunnableCode = ({children, run = false, showStats = true}) => {
  const [results, setResults] = useState(null);
  const [error, setError] = useState(null);
  const [loading, setLoading] = useState(false);
  const [showResults, setShowResults] = useState(false);
  const [stats, setStats] = useState(null);
  const [isDark, setIsDark] = useState(false);
  const [hoveredRow, setHoveredRow] = useState(-1);
  const codeRef = useRef(null);
  useEffect(() => {
    if (typeof window !== "undefined") {
      const check = () => setIsDark(document.documentElement.classList.contains("dark"));
      check();
      const observer = new MutationObserver(check);
      observer.observe(document.documentElement, {
        attributes: true,
        attributeFilter: ["class"]
      });
      return () => observer.disconnect();
    }
  }, []);
  useEffect(() => {
    if (codeRef.current) {
      const block = codeRef.current.querySelector(".code-block");
      if (block) {
        block.style.marginBottom = "0";
        block.style.marginTop = "0";
        block.style.borderBottomLeftRadius = "0";
        block.style.borderBottomRightRadius = "0";
      }
    }
  });
  const getSqlText = () => {
    if (!codeRef.current) return "";
    const code = codeRef.current.querySelector("code");
    return (code || codeRef.current).textContent.trim();
  };
  const executeQuery = async () => {
    const sql = getSqlText();
    if (!sql) return;
    setLoading(true);
    setError(null);
    setResults(null);
    setShowResults(true);
    try {
      const cleanQuery = sql.replace(/;$/, "").trim();
      const params = new URLSearchParams({
        query: cleanQuery,
        default_format: "JSONCompact",
        result_overflow_mode: "break",
        read_overflow_mode: "break",
        allow_experimental_analyzer: "1"
      });
      const res = await fetch(`https://sql-clickhouse.clickhouse.com/?${params.toString()}`, {
        method: "POST",
        headers: {
          Authorization: `Basic ${btoa(`demo:`)}`
        }
      });
      const text = await res.text();
      if (!res.ok) {
        setError(text || `HTTP ${res.status}`);
        setLoading(false);
        return;
      }
      const json = JSON.parse(text);
      setResults(json);
      setStats(json.statistics || null);
    } catch (err) {
      setError(err.message || "Ошибка выполнения запроса");
    }
    setLoading(false);
  };
  useEffect(() => {
    if (run) executeQuery();
  }, []);
  const formatRows = n => {
    if (n >= 1e9) return `${(n / 1e9).toFixed(1)}B`;
    if (n >= 1e6) return `${(n / 1e6).toFixed(1)}M`;
    if (n >= 1e3) return `${(n / 1e3).toFixed(1)}K`;
    return String(n);
  };
  const formatBytes = b => {
    if (b >= 1e9) return `${(b / 1e9).toFixed(2)} GB`;
    if (b >= 1e6) return `${(b / 1e6).toFixed(2)} MB`;
    if (b >= 1e3) return `${(b / 1e3).toFixed(2)} KB`;
    return `${b} B`;
  };
  const isNumericType = type => {
    return (/^(UInt|Int|Float|Decimal)/).test(type);
  };
  const isHyperlink = value => {
    return typeof value === "string" && (/^https?:\/\//).test(value);
  };
  const computeColumnExtremes = (meta, data) => {
    const extremes = {};
    for (let i = 0; i < meta.length; i++) {
      if (isNumericType(meta[i].type)) {
        let min = Infinity, max = -Infinity;
        for (const row of data) {
          const v = Number(row[i]);
          if (!isNaN(v)) {
            if (v < min) min = v;
            if (v > max) max = v;
          }
        }
        if (max > -Infinity) {
          extremes[i] = {
            min,
            max
          };
        }
      }
    }
    return extremes;
  };
  const computeColumnWidths = (meta, data) => {
    const lengths = meta.map((col, i) => {
      const headerLen = col.name.length + col.type.length + 1;
      let maxData = 0;
      for (const row of data) {
        const v = row[i];
        const len = v === null ? 4 : String(v).length;
        if (len > maxData) maxData = len;
      }
      return Math.max(headerLen, maxData);
    });
    const total = lengths.reduce((s, l) => s + l, 0);
    return lengths.map(l => `${(l / total * 100).toFixed(1)}%`);
  };
  const copyResultsAsTSV = () => {
    if (!results || !results.meta || !results.data) return;
    const header = results.meta.map(col => col.name).join("\t");
    const rows = results.data.map(row => row.map(cell => cell === null ? "NULL" : String(cell)).join("\t"));
    const tsv = [header, ...rows].join("\n");
    navigator.clipboard.writeText(tsv);
  };
  const borderColor = isDark ? "rgba(255,255,255,0.15)" : "#e5e7eb";
  const bgColor = isDark ? "rgba(255,255,255,0.05)" : "#f9fafb";
  const headerBg = isDark ? "#2a2a2a" : "#f3f4f6";
  const textColor = isDark ? "#e5e7eb" : "#1f2937";
  const mutedColor = isDark ? "#d1d5db" : "#6b7280";
  const accentColor = isDark ? "#FAFF69" : "#323232";
  const accentTextColor = isDark ? "#000" : "#fff";
  const barColor = isDark ? "#35372f" : "#d2d2d2";
  const cellBg = isDark ? "#1f201b" : "#ffffff";
  const cellBgHover = isDark ? "lch(15.8 0 0)" : "#f0f0f0";
  const extremes = results && results.meta && results.data ? computeColumnExtremes(results.meta, results.data) : {};
  const colWidths = results && results.meta && results.data ? computeColumnWidths(results.meta, results.data) : [];
  const getCellBarStyle = (cell, ci, ri) => {
    if (cell === null) return null;
    const colMeta = results.meta[ci];
    if (!isNumericType(colMeta.type) || !extremes[ci] || results.data.length <= 1 || extremes[ci].max <= 0) return null;
    const ratio = 100 * Number(cell) / extremes[ci].max;
    const bg = ri === hoveredRow ? cellBgHover : cellBg;
    return {
      background: `linear-gradient(to right, ${barColor} 0%, ${barColor} ${ratio}%, ${bg} ${ratio}%, ${bg} 100%)`
    };
  };
  const renderCell = (cell, ci) => {
    if (cell === null) {
      return <span style={{
        color: mutedColor,
        fontStyle: "italic"
      }}>NULL</span>;
    }
    const value = String(cell);
    if (isHyperlink(value)) {
      return <a href={value} target="_blank" rel="noopener noreferrer" style={{
        color: accentColor,
        textDecoration: "underline",
        cursor: "pointer"
      }}>
          {value}
        </a>;
    }
    return value;
  };
  return <div className="not-prose" style={{
    margin: "1rem 0",
    width: "100%",
    boxSizing: "border-box",
    contain: "inline-size"
  }}>
      {}
      <div>
        <div ref={codeRef}>{children}</div>

        {}
        <div style={{
    display: "flex",
    justifyContent: "space-between",
    alignItems: "center",
    padding: "6px 12px",
    backgroundColor: headerBg,
    borderWidth: "0 1px 1px 1px",
    borderStyle: "solid",
    borderColor: isDark ? "rgba(255,255,255,0.1)" : "rgba(11,11,11,0.1)",
    borderRadius: "0 0 4px 4px"
  }}>
          <div style={{
    display: "flex",
    alignItems: "center",
    gap: "12px"
  }}>
            {results && <button onClick={() => setShowResults(!showResults)} style={{
    background: "none",
    border: "none",
    cursor: "pointer",
    color: mutedColor,
    fontSize: "12px",
    padding: "2px 4px"
  }}>
                {showResults ? "▼ Скрыть результаты" : "▶ Показать результаты"}
              </button>}
            {showStats && stats && <span style={{
    fontSize: "11px",
    color: mutedColor,
    fontStyle: "italic"
  }}>
                Прочитано {formatRows(stats.rows_read)} строк, {formatBytes(stats.bytes_read)} за {stats.elapsed.toFixed(3)}с
              </span>}
          </div>
          <button onClick={() => executeQuery()} disabled={loading} style={{
    display: "flex",
    alignItems: "center",
    gap: "6px",
    padding: "4px 14px",
    borderRadius: "4px",
    border: "none",
    cursor: loading ? "wait" : "pointer",
    backgroundColor: accentColor,
    color: accentTextColor,
    fontSize: "12px",
    fontWeight: 600
  }}>
            {loading ? <span>Выполняется...</span> : <>
                <span style={{
    fontSize: "10px"
  }}>▶</span>
                <span>Выполнить</span>
              </>}
          </button>
        </div>
      </div>

      {}
      {showResults && <div className="not-prose" style={{
    marginTop: "8px",
    maxHeight: "350px",
    overflow: "auto",
    border: `1px solid ${borderColor}`,
    borderRadius: "4px"
  }}>
          <div>
            {loading && <div style={{
    padding: "24px",
    textAlign: "center",
    color: mutedColor
  }}>Выполнение запроса...</div>}

            {error && <div style={{
    padding: "12px 16px",
    color: "#ef4444",
    backgroundColor: isDark ? "rgba(239,68,68,0.1)" : "#fef2f2",
    fontSize: "13px",
    fontFamily: "monospace",
    whiteSpace: "pre-wrap"
  }}>
                {error}
              </div>}

            {results && results.meta && results.data && <div style={{
    display: "grid",
    gridTemplateColumns: colWidths.join(" "),
    width: "100%",
    fontSize: "13px",
    fontFamily: 'ui-monospace, SFMono-Regular, "SF Mono", Menlo, Consolas, monospace'
  }}>
                {results.meta.map((col, i) => <div key={`h-${i}`} style={{
    position: "sticky",
    top: 0,
    zIndex: 1,
    padding: "6px 12px",
    textAlign: isNumericType(col.type) && results.meta.length > 1 ? "right" : "left",
    backgroundColor: headerBg,
    borderBottom: `1px solid ${borderColor}`,
    color: textColor,
    fontWeight: 600,
    fontSize: "12px",
    whiteSpace: "nowrap",
    overflow: "hidden",
    textOverflow: "ellipsis"
  }}>
                    {col.name}
                    <span style={{
    color: mutedColor,
    fontWeight: 400,
    marginLeft: "4px",
    fontSize: "10px"
  }}>{col.type}</span>
                  </div>)}
                {results.data.map((row, ri) => row.map((cell, ci) => <div key={`${ri}-${ci}`} onMouseEnter={() => setHoveredRow(ri)} onMouseLeave={() => setHoveredRow(-1)} style={{
    padding: "4px 12px",
    color: textColor,
    whiteSpace: "nowrap",
    overflow: "hidden",
    textOverflow: "ellipsis",
    textAlign: isNumericType(results.meta[ci].type) && results.meta.length > 1 ? "right" : "left",
    borderBottom: `1px solid ${borderColor}`,
    backgroundColor: ri === hoveredRow ? cellBgHover : ri % 2 === 0 ? "transparent" : bgColor,
    transition: "background-color 0.1s",
    ...getCellBarStyle(cell, ci, ri)
  }}>
                      {renderCell(cell, ci)}
                    </div>))}
              </div>}

            {results && results.data && <div style={{
    display: "flex",
    justifyContent: "space-between",
    alignItems: "center",
    padding: "4px 12px",
    fontSize: "11px",
    color: mutedColor,
    borderTop: `1px solid ${borderColor}`,
    backgroundColor: headerBg
  }}>
                <span>
                  {results.rows} {results.rows === 1 ? "строка" : results.rows >= 2 && results.rows <= 4 ? "строки" : "строк"}
                </span>
                <button onClick={copyResultsAsTSV} style={{
    background: "none",
    border: "none",
    cursor: "pointer",
    color: mutedColor,
    fontSize: "11px",
    padding: "2px 6px",
    borderRadius: "3px"
  }} onMouseEnter={e => e.target.style.color = textColor} onMouseLeave={e => e.target.style.color = mutedColor}>
                  ⧉ Копировать TSV
                </button>
              </div>}
          </div>
        </div>}
    </div>;
};

export const Image = ({img, alt, size}) => {
  return <Frame>
      <img src={img} alt={alt} />
    </Frame>;
};

<div id="what-are-table-partitions-in-clickhouse">
  ## Что такое партиции таблицы в ClickHouse?
</div>

<br />

Партиции объединяют [части данных](/ru/concepts/core-concepts/parts) таблицы из [семейства движков MergeTree](/ru/reference/engines/table-engines/mergetree-family/index) в упорядоченные логические единицы. Это способ организации данных в соответствии с определёнными критериями, такими как временные диапазоны, категории или другие ключевые атрибуты. Такие логические единицы упрощают управление данными, выполнение запросов и оптимизацию.

<div id="partition-by">
  ### PARTITION BY
</div>

Партиционирование можно включить при первоначальном определении таблицы с помощью [секции PARTITION BY](/ru/reference/engines/table-engines/mergetree-family/custom-partitioning-key). Эта секция может содержать SQL-выражение над любыми столбцами, результат которого определяет, к какой партиции принадлежит строка.

Чтобы проиллюстрировать это, мы [дополним](https://sql.clickhouse.com/?query=U0hPVyBDUkVBVEUgVEFCTEUgdWsudWtfcHJpY2VfcGFpZF9zaW1wbGVfcGFydGl0aW9uZWQ\&run_query=true\&tab=results) пример таблицы [Что такое части таблицы](/ru/concepts/core-concepts/parts), добавив секцию `PARTITION BY toStartOfMonth(date)`, которая распределяет части таблицы по месяцам продажи недвижимости:

```sql theme={null}
CREATE TABLE uk.uk_price_paid_simple_partitioned
(
    date Date,
    town LowCardinality(String),
    street LowCardinality(String),
    price UInt32
)
ENGINE = MergeTree
ORDER BY (town, street)
PARTITION BY toStartOfMonth(date);
```

Вы можете [запросить данные из этой таблицы](https://sql.clickhouse.com/?query=U0VMRUNUICogRlJPTSB1ay51a19wcmljZV9wYWlkX3NpbXBsZV9wYXJ0aXRpb25lZA\&run_query=true\&tab=results) в нашей Песочнице ClickHouse.

<div id="structure-on-disk">
  ### Структура на диске
</div>

Каждый раз, когда в таблицу вставляется набор строк, ClickHouse вместо создания одной-единственной части данных, содержащей все вставленные строки (как описано [здесь](/ru/concepts/core-concepts/parts)), создает как минимум одну новую часть данных для каждого уникального значения ключа партиционирования среди вставленных строк (не менее [одной](/ru/reference/settings/session-settings#max_insert_block_size)):

<Image img="https://mintcdn.com/private-7c7dfe99-mintlify-fbfa8bee/rF8ZX2ZZNpnwXrqH/images/managing-data/core-concepts/partitions.png?fit=max&auto=format&n=rF8ZX2ZZNpnwXrqH&q=85&s=697116401d8f11285518e0b1ace63ffd" size="lg" alt="ОБРАБОТКА INSERT" width="3324" height="2270" data-path="images/managing-data/core-concepts/partitions.png" />

<br />

Сначала сервер ClickHouse разделяет строки из примера вставки с 4 строками, схематично показанного на диаграмме выше, по значению ключа партиционирования `toStartOfMonth(date)`.
Затем для каждой выявленной партиции строки обрабатываются [как обычно](/ru/concepts/core-concepts/parts) в несколько последовательных шагов (① Сортировка, ② Разбиение на столбцы, ③ Сжатие, ④ Запись на диск).

Обратите внимание: при включенном партиционировании ClickHouse автоматически создает [индексы MinMax](https://github.com/ClickHouse/ClickHouse/blob/dacc8ebb0dac5bbfce5a7541e7fc70f26f7d5065/src/Storages/MergeTree/IMergeTreeDataPart.h#L341) для каждой части данных. Это просто файлы для каждого столбца таблицы, используемого в выражении ключа партиционирования, которые содержат минимальное и максимальное значения этого столбца в пределах части данных.

<div id="per-partition-merges">
  ### Слияния внутри партиций
</div>

При включенном партиционировании ClickHouse выполняет [слияния](/ru/concepts/core-concepts/merges) частей данных только в пределах одной партиции, но не между разными партициями. Покажем это на примере таблицы выше:

<Image img="https://mintcdn.com/private-7c7dfe99-mintlify-fbfa8bee/rF8ZX2ZZNpnwXrqH/images/managing-data/core-concepts/merges_with_partitions.png?fit=max&auto=format&n=rF8ZX2ZZNpnwXrqH&q=85&s=8792d49d8dfa106f63224cd839ed3540" size="lg" alt="PART MERGES" width="2480" height="1870" data-path="images/managing-data/core-concepts/merges_with_partitions.png" />

<br />

Как показано на диаграмме выше, части, принадлежащие разным партициям, никогда не сливаются. Если выбран ключ партиционирования с высокой мощностью, то части, распределенные по тысячам партиций, никогда не будут кандидатами на слияние, что приведет к превышению заранее заданных ограничений и вызовет пресловутую ошибку `Too many parts`. Решение этой проблемы простое: выберите разумный ключ партиционирования с [мощностью менее 1000..10000](https://github.com/ClickHouse/ClickHouse/blob/ffc5b2c56160b53cf9e5b16cfb73ba1d956f7ce4/src/Storages/MergeTree/MergeTreeDataWriter.cpp#L121).

<div id="monitoring-partitions">
  ## Мониторинг партиций
</div>

Вы можете [выполнить запрос](https://sql.clickhouse.com/?query=U0VMRUNUIERJU1RJTkNUIF9wYXJ0aXRpb25fdmFsdWUgQVMgcGFydGl0aW9uCkZST00gdWsudWtfcHJpY2VfcGFpZF9zaW1wbGVfcGFydGl0aW9uZWQKT1JERVIgQlkgcGFydGl0aW9uIEFTQw\&run_query=true\&tab=results), чтобы получить список всех существующих уникальных партиций нашей тестовой таблицы, используя [виртуальный столбец](/ru/reference/engines/table-engines/index#table_engines-virtual_columns) `_partition_value`:

<RunnableCode>
  ```sql theme={null}
  SELECT DISTINCT _partition_value AS partition
  FROM uk.uk_price_paid_simple_partitioned
  ORDER BY partition ASC;
  ```
</RunnableCode>

Кроме того, ClickHouse отслеживает все части и партиции всех таблиц в системной таблице [system.parts](/ru/reference/system-tables/parts), а следующий запрос [возвращает](https://sql.clickhouse.com/?query=U0VMRUNUCiAgICBwYXJ0aXRpb24sCiAgICBjb3VudCgpIEFTIHBhcnRzLAogICAgc3VtKHJvd3MpIEFTIHJvd3MKRlJPTSBzeXN0ZW0ucGFydHMKV0hFUkUgKGRhdGFiYXNlID0gJ3VrJykgQU5EIChgdGFibGVgID0gJ3VrX3ByaWNlX3BhaWRfc2ltcGxlX3BhcnRpdGlvbmVkJykgQU5EIGFjdGl2ZQpHUk9VUCBCWSBwYXJ0aXRpb24KT1JERVIgQlkgcGFydGl0aW9uIEFTQzs\&run_query=true\&tab=results) для приведённой выше тестовой таблицы список всех партиций, а также текущее количество активных частей и суммарное число строк в этих частях для каждой партиции:

<RunnableCode>
  ```sql theme={null}
  SELECT
      partition,
      count() AS parts,
      sum(rows) AS rows
  FROM system.parts
  WHERE (database = 'uk') AND (`table` = 'uk_price_paid_simple_partitioned') AND active
  GROUP BY partition
  ORDER BY partition ASC;
  ```
</RunnableCode>

<div id="what-are-table-partitions-used-for">
  ## Для чего нужны партиции таблиц?
</div>

<div id="data-management">
  ### Управление данными
</div>

В ClickHouse партиционирование в первую очередь используется для управления данными. Благодаря логической организации данных на основе выражения партиционирования каждой партицией можно управлять независимо. Например, схема партиционирования в приведённой выше таблице позволяет реализовать сценарии, в которых в основной таблице хранятся только данные за последние 12 месяцев, а более старые данные автоматически удаляются с помощью [правила TTL](/ru/concepts/features/operations/delete/ttl) (см. добавленную последнюю строку DDL-оператора):

```sql theme={null}
CREATE TABLE uk.uk_price_paid_simple_partitioned
(
    date Date,
    town LowCardinality(String),
    street LowCardinality(String),
    price UInt32
)
ENGINE = MergeTree
PARTITION BY toStartOfMonth(date)
ORDER BY (town, street)
TTL date + INTERVAL 12 MONTH DELETE;
```

Поскольку таблица разбита на партиции по `toStartOfMonth(date)`, целые партиции (наборы [частей таблицы](/ru/concepts/core-concepts/parts)), подпадающие под условие TTL, будут удаляться, что повышает эффективность очистки [без переписывания частей](/ru/reference/statements/alter/index#mutations).

Аналогично, вместо удаления старых данных их можно автоматически и эффективно перемещать на более экономичный [уровень хранилища](/ru/integrations/connectors/data-ingestion/AWS/integrating-s3-with-clickhouse#storage-tiers):

```sql theme={null}
CREATE TABLE uk.uk_price_paid_simple_partitioned
(
    date Date,
    town LowCardinality(String),
    street LowCardinality(String),
    price UInt32
)
ENGINE = MergeTree
PARTITION BY toStartOfMonth(date)
ORDER BY (town, street)
TTL date + INTERVAL 12 MONTH TO VOLUME 'slow_but_cheap';
```

<div id="query-optimization">
  ### Оптимизация запросов
</div>

Партиции могут повысить производительность запросов, но это сильно зависит от характера доступа к данным. Если запросы обращаются только к нескольким партициям (в идеале — к одной), производительность может улучшиться. На практике это обычно полезно только тогда, когда ключ партиционирования не входит в первичный ключ, а фильтрация выполняется именно по нему, как показано в примере запроса ниже.

<RunnableCode>
  ```sql theme={null}
  SELECT MAX(price) AS highest_price
  FROM uk.uk_price_paid_simple_partitioned
  WHERE date >= '2020-12-01'
    AND date <= '2020-12-31'
    AND town = 'LONDON';
  ```
</RunnableCode>

Этот запрос выполняется для таблицы из примера выше и [вычисляет](https://sql.clickhouse.com/?query=U0VMRUNUIE1BWChwcmljZSkgQVMgaGlnaGVzdF9wcmljZQpGUk9NIHVrLnVrX3ByaWNlX3BhaWRfc2ltcGxlX3BhcnRpdGlvbmVkCldIRVJFIGRhdGUgPj0gJzIwMjAtMTItMDEnCiAgQU5EIGRhdGUgPD0gJzIwMjAtMTItMzEnCiAgQU5EIHRvd24gPSAnTE9ORE9OJzs\&run_query=true\&tab=results) максимальную цену среди всех проданных объектов недвижимости в Лондоне за декабрь 2020 года, используя фильтрацию как по столбцу (`date`), который входит в ключ партиционирования таблицы, так и по столбцу (`town`), который входит в первичный ключ таблицы (при этом `date` не является частью первичного ключа).

ClickHouse обрабатывает этот запрос, применяя последовательность методов отсеивания данных, чтобы не обрабатывать нерелевантные данные:

<Image img="https://mintcdn.com/private-7c7dfe99-mintlify-fbfa8bee/rF8ZX2ZZNpnwXrqH/images/managing-data/core-concepts/partition-pruning.png?fit=max&auto=format&n=rF8ZX2ZZNpnwXrqH&q=85&s=4b3b108e73064db0364e3411683a706c" size="lg" alt="PART MERGES 2" width="2478" height="1886" data-path="images/managing-data/core-concepts/partition-pruning.png" />

<br />

① **Отсечение партиций**: [MinMax indexes](/ru/concepts/core-concepts/partitions#what-are-table-partitions-in-clickhouse) используются, чтобы игнорировать целые партиции (наборы частей), которые заведомо не могут соответствовать фильтру запроса по столбцам, используемым в ключе партиционирования таблицы.

② **Отсечение гранул**: Для оставшихся частей данных после шага ① используется их [первичный индекс](/ru/guides/clickhouse/data-modelling/sparse-primary-indexes), чтобы игнорировать все [гранулы](/ru/guides/clickhouse/data-modelling/sparse-primary-indexes#data-is-organized-into-granules-for-parallel-data-processing) (блоки строк), которые заведомо не могут соответствовать фильтру запроса по столбцам, используемым в первичном ключе таблицы.

Мы можем увидеть эти шаги отсеивания данных, [изучив](https://sql.clickhouse.com/?query=RVhQTEFJTiBpbmRleGVzID0gMQpTRUxFQ1QgTUFYKHByaWNlKSBBUyBoaWdoZXN0X3ByaWNlCkZST00gdWsudWtfcHJpY2VfcGFpZF9zaW1wbGVfcGFydGl0aW9uZWQKV0hFUkUgZGF0ZSA-PSAnMjAyMC0xMi0wMScKICBBTkQgZGF0ZSA8PSAnMjAyMC0xMi0zMScKICBBTkQgdG93biA9ICdMT05ET04nOw\&run_query=true\&tab=results) физический план выполнения для нашего примера запроса выше с помощью оператора [EXPLAIN](/ru/reference/statements/explain) :

```sql style="fontSize:13px" theme={null}
EXPLAIN indexes = 1
SELECT MAX(price) AS highest_price
FROM uk.uk_price_paid_simple_partitioned
WHERE date >= '2020-12-01'
  AND date <= '2020-12-31'
  AND town = 'LONDON';
```

```response theme={null}
    ┌─explain──────────────────────────────────────────────────────────────────────────────────────────────────────┐
 1. │ Expression ((Project names + Projection))                                                                    │
 2. │   Aggregating                                                                                                │
 3. │     Expression (Before GROUP BY)                                                                             │
 4. │       Expression                                                                                             │
 5. │         ReadFromMergeTree (uk.uk_price_paid_simple_partitioned)                                              │
 6. │         Indexes:                                                                                             │
 7. │           MinMax                                                                                             │
 8. │             Keys:                                                                                            │
 9. │               date                                                                                           │
10. │             Condition: and((date in (-Inf, 18627]), (date in [18597, +Inf)))                                 │
11. │             Parts: 1/436                                                                                     │
12. │             Granules: 11/3257                                                                                │
13. │           Partition                                                                                          │
14. │             Keys:                                                                                            │
15. │               toStartOfMonth(date)                                                                           │
16. │             Condition: and((toStartOfMonth(date) in (-Inf, 18597]), (toStartOfMonth(date) in [18597, +Inf))) │
17. │             Parts: 1/1                                                                                       │
18. │             Granules: 11/11                                                                                  │
19. │           PrimaryKey                                                                                         │
20. │             Keys:                                                                                            │
21. │               town                                                                                           │
22. │             Condition: (town in ['LONDON', 'LONDON'])                                                        │
23. │             Parts: 1/1                                                                                       │
24. │             Granules: 1/11                                                                                   │
    └──────────────────────────────────────────────────────────────────────────────────────────────────────────────┘
```

Вывод выше показывает:

① Отсечение партиций: строки 7–18 приведённого выше вывода EXPLAIN показывают, что ClickHouse сначала использует [индекс MinMax](/ru/concepts/core-concepts/partitions#what-are-table-partitions-in-clickhouse) поля `date`, чтобы выявить 11 из 3257 существующих [гранул](/ru/guides/clickhouse/data-modelling/sparse-primary-indexes#data-is-organized-into-granules-for-parallel-data-processing) (блоков строк), хранящихся в 1 из 436 существующих активных частей данных, которые содержат строки, соответствующие фильтру `date` в запросе.

② Отсечение гранул: строки 19–24 приведённого выше вывода EXPLAIN показывают, что затем ClickHouse использует [первичный индекс](/ru/guides/clickhouse/data-modelling/sparse-primary-indexes) (созданный по полю `town`) части данных, определённой на шаге ①, чтобы ещё сильнее сократить число гранул (содержащих строки, которые потенциально также соответствуют фильтру `town` в запросе) — с 11 до 1. Это также отражено в выводе ClickHouse-client, который мы привели выше для выполненного запроса:

```response theme={null}
... Elapsed: 0.006 sec. Processed 8.19 thousand rows, 57.34 KB (1.36 million rows/s., 9.49 MB/s.)
Peak memory usage: 2.73 MiB.
```

Это означает, что ClickHouse просканировал и обработал 1 гранулу (блок из [8192](/ru/reference/settings/merge-tree-settings#index_granularity) строк) за 6 миллисекунд, чтобы вычислить результат запроса.

<div id="partitioning-is-primarily-a-data-management-feature">
  ### Партиционирование — это прежде всего средство управления данными
</div>

Имейте в виду, что запросы по всем партициям обычно выполняются медленнее, чем тот же запрос к таблице без партиционирования.

При партиционировании данные обычно распределяются по большему числу частей данных, из-за чего ClickHouse часто приходится сканировать и обрабатывать больший объем данных.

Это можно продемонстрировать, выполнив один и тот же запрос как для таблицы из примера [Что такое части таблицы](/ru/concepts/core-concepts/parts) (без партиционирования), так и для нашей текущей таблицы из примера выше (с партиционированием). Обе таблицы [содержат](https://sql.clickhouse.com/?query=U0VMRUNUCiAgICB0YWJsZSwKICAgIHN1bShyb3dzKSBBUyByb3dzCkZST00gc3lzdGVtLnBhcnRzCldIRVJFIChkYXRhYmFzZSA9ICd1aycpIEFORCAoYHRhYmxlYCBJTiBbJ3VrX3ByaWNlX3BhaWRfc2ltcGxlJywgJ3VrX3ByaWNlX3BhaWRfc2ltcGxlX3BhcnRpdGlvbmVkJ10pIEFORCBhY3RpdmUKR1JPVVAgQlkgdGFibGU7\&run_query=true\&tab=results) одни и те же данные и одинаковое количество строк:

<RunnableCode>
  ```sql theme={null}
  SELECT
      table,
      sum(rows) AS rows
  FROM system.parts
  WHERE (database = 'uk') AND (table IN ['uk_price_paid_simple', 'uk_price_paid_simple_partitioned']) AND active
  GROUP BY table;
  ```
</RunnableCode>

Однако в таблице с партиционированием [больше](https://sql.clickhouse.com/?query=U0VMRUNUCiAgICB0YWJsZSwKICAgIGNvdW50KCkgQVMgcGFydHMKRlJPTSBzeXN0ZW0ucGFydHMKV0hFUkUgKGRhdGFiYXNlID0gJ3VrJykgQU5EIChgdGFibGVgIElOIFsndWtfcHJpY2VfcGFpZF9zaW1wbGUnLCAndWtfcHJpY2VfcGFpZF9zaW1wbGVfcGFydGl0aW9uZWQnXSkgQU5EIGFjdGl2ZQpHUk9VUCBCWSB0YWJsZTs\&run_query=true\&tab=results) активных [частей данных](/ru/concepts/core-concepts/parts), потому что, как упоминалось выше, ClickHouse [выполняет слияние](/ru/concepts/core-concepts/parts) части данных только внутри партиций, но не между ними:

<RunnableCode>
  ```sql theme={null}
  SELECT
      table,
      count() AS parts
  FROM system.parts
  WHERE (database = 'uk') AND (table IN ['uk_price_paid_simple', 'uk_price_paid_simple_partitioned']) AND active
  GROUP BY table;

  ```
</RunnableCode>

Как показано выше, партиционированная таблица `uk_price_paid_simple_partitioned` имеет более 600 партиций и, следовательно, 600 306 активных частей данных. В то же время в таблице без партиционирования `uk_price_paid_simple` все [исходные](/ru/concepts/core-concepts/parts) части данных могли быть объединены фоновыми слияниями в одну активную часть.

Если [посмотреть](https://sql.clickhouse.com/?query=RVhQTEFJTiBpbmRleGVzID0gMQpTRUxFQ1QgTUFYKHByaWNlKSBBUyBoaWdoZXN0X3ByaWNlCkZST00gdWsudWtfcHJpY2VfcGFpZF9zaW1wbGVfcGFydGl0aW9uZWQKV0hFUkUgdG93biA9ICdMT05ET04nOw\&run_query=true\&tab=results) физический план выполнения нашего запроса из примера выше без фильтра по партиции для партиционированной таблицы с помощью оператора [EXPLAIN](/ru/reference/statements/explain), то в строках 19 и 20 приведенного ниже вывода видно, что ClickHouse определил 671 из 3257 существующих [гранул](/ru/guides/clickhouse/data-modelling/sparse-primary-indexes#data-is-organized-into-granules-for-parallel-data-processing) (блоков строк), распределенных по 431 из 436 существующих активных частей данных, которые потенциально содержат строки, соответствующие фильтру запроса, и поэтому будут просканированы и обработаны движком запросов:

```sql theme={null}
EXPLAIN indexes = 1
SELECT MAX(price) AS highest_price
FROM uk.uk_price_paid_simple_partitioned
WHERE town = 'LONDON';
```

```response theme={null}
    ┌─explain─────────────────────────────────────────────────────────┐
 1. │ Expression ((Project names + Projection))                       │
 2. │   Aggregating                                                   │
 3. │     Expression (Before GROUP BY)                                │
 4. │       Expression                                                │
 5. │         ReadFromMergeTree (uk.uk_price_paid_simple_partitioned) │
 6. │         Indexes:                                                │
 7. │           MinMax                                                │
 8. │             Condition: true                                     │
 9. │             Parts: 436/436                                      │
10. │             Granules: 3257/3257                                 │
11. │           Partition                                             │
12. │             Condition: true                                     │
13. │             Parts: 436/436                                      │
14. │             Granules: 3257/3257                                 │
15. │           PrimaryKey                                            │
16. │             Keys:                                               │
17. │               town                                              │
18. │             Condition: (town in ['LONDON', 'LONDON'])           │
19. │             Parts: 431/436                                      │
20. │             Granules: 671/3257                                  │
    └─────────────────────────────────────────────────────────────────┘
```

Физический план выполнения того же примерного запроса для таблицы без партиций [показывает](https://sql.clickhouse.com/?query=RVhQTEFJTiBpbmRleGVzID0gMQpTRUxFQ1QgTUFYKHByaWNlKSBBUyBoaWdoZXN0X3ByaWNlCkZST00gdWsudWtfcHJpY2VfcGFpZF9zaW1wbGUKV0hFUkUgdG93biA9ICdMT05ET04nOw\&run_query=true\&tab=results) в строках 11 и 12 приведённого ниже вывода, что ClickHouse выявил 241 из 3083 существующих блоков строк в единственной активной части данных таблицы, которые потенциально содержат строки, соответствующие фильтру запроса:

```sql theme={null}
EXPLAIN indexes = 1
SELECT MAX(price) AS highest_price
FROM uk.uk_price_paid_simple
WHERE town = 'LONDON';
```

```response theme={null}
    ┌─explain───────────────────────────────────────────────┐
 1. │ Expression ((Project names + Projection))             │
 2. │   Aggregating                                         │
 3. │     Expression (Before GROUP BY)                      │
 4. │       Expression                                      │
 5. │         ReadFromMergeTree (uk.uk_price_paid_simple)   │
 6. │         Indexes:                                      │
 7. │           PrimaryKey                                  │
 8. │             Keys:                                     │
 9. │               town                                    │
10. │             Condition: (town in ['LONDON', 'LONDON']) │
11. │             Parts: 1/1                                │
12. │             Granules: 241/3083                        │
    └───────────────────────────────────────────────────────┘
```

При [выполнении](https://sql.clickhouse.com/?query=U0VMRUNUIE1BWChwcmljZSkgQVMgaGlnaGVzdF9wcmljZQpGUk9NIHVrLnVrX3ByaWNlX3BhaWRfc2ltcGxlX3BhcnRpdGlvbmVkCldIRVJFIHRvd24gPSAnTE9ORE9OJzs\&run_query=true\&tab=results) запроса к партиционированной версии таблицы ClickHouse сканирует и обрабатывает 671 блок строк (\~ 5,5 миллиона строк) за 90 миллисекунд:

```sql theme={null}
SELECT MAX(price) AS highest_price
FROM uk.uk_price_paid_simple_partitioned
WHERE town = 'LONDON';
```

```response theme={null}
┌─highest_price─┐
│     594300000 │ -- 594.30 million
└───────────────┘

1 row in set. Elapsed: 0.090 sec. Processed 5.48 million rows, 27.95 MB (60.66 million rows/s., 309.51 MB/s.)
Peak memory usage: 163.44 MiB.
```

В то время как при [выполнении](https://sql.clickhouse.com/?query=U0VMRUNUIE1BWChwcmljZSkgQVMgaGlnaGVzdF9wcmljZQpGUk9NIHVrLnVrX3ByaWNlX3BhaWRfc2ltcGxlCldIRVJFIHRvd24gPSAnTE9ORE9OJzs\&run_query=true\&tab=results) запроса к непартиционированной таблице ClickHouse сканирует и обрабатывает 241 блок (\~ 2 миллиона строк) за 12 миллисекунд:

```sql theme={null}
SELECT MAX(price) AS highest_price
FROM uk.uk_price_paid_simple
WHERE town = 'LONDON';
```

```response theme={null}
┌─highest_price─┐
│     594300000 │ -- 594,30 млн
└───────────────┘

1 row in set. Elapsed: 0.012 sec. Processed 1.97 million rows, 9.87 MB (162.23 million rows/s., 811.17 MB/s.)
Peak memory usage: 62.02 MiB.
```
