Примечание
Для доступа к этой странице требуется авторизация. Вы можете попробовать войти или изменить каталоги.
Для доступа к этой странице требуется авторизация. Вы можете попробовать изменить каталоги.
Рекомендации и примеры индексирования таблиц в выделенном пуле SQL в Azure Synapse Analytics.
Типы индексов
Выделенный пул SQL предлагает несколько вариантов индексирования, включая кластеризованные индексы columnstore, кластеризованные индексы и некластеризованные индексы, а также параметр nonindex, также известный как куча.
Сведения о том, как создать таблицу с индексом, см. в документации по операции CREATE TABLE (для выделенного пула SQL).
Кластеризованные индексы Columnstore
Если для таблицы не заданы параметры индексирования, по умолчанию выделенный пул SQL создает кластеризованный индекс columnstore. Кластерированные таблицы Columnstore предоставляют не только самый высокий уровень сжатия данных, но и высочайшую производительность запросов. Обычно эти таблицы более эффективны, чем таблицы с кластеризованными индексами и таблицы без кластеризованных индексов, и их рекомендуется использовать в больших таблицах. Кластеризованное хранилище столбцов — наилучший выбор, если вы не уверены, как индексировать таблицу.
Чтобы создать кластеризованную таблицу columnstore, укажите CLUSTERED COLUMNSTORE INDEX
в предложении WITH или оставьте предложение WITH отключенным:
CREATE TABLE myTable
(
id int NOT NULL,
lastName varchar(20),
zipCode varchar(6)
)
WITH ( CLUSTERED COLUMNSTORE INDEX );
Существует несколько сценариев, в которых кластеризованное хранилище столбцов может оказаться не хорошим вариантом.
- Таблицы columnstore не поддерживают типы данных VARCHAR(MAX), NVARCHAR(MAX) и VARBINARY(MAX). В этом случае рекомендуется использовать кучу или кластеризованный индекс.
- Таблицы columnstore могут быть менее эффективными для данных временного характера. Рассмотрим кучу и временные таблицы.
- Небольшие таблицы, содержащие менее 60 млн строк. В этом случае рекомендуется использовать таблицы без кластеризованных индексов.
Таблицы без кластеризованных индексов
При временном размещении данных в выделенном пуле SQL вы можете обнаружить, что использование таблицы без кластеризации ускоряет общий процесс. Это связано с тем, что загрузка в кучи выполняется быстрее, чем в индексные таблицы, и в некоторых случаях последующее считывание можно выполнить из кэша. Если данные загружаются только на стадию перед выполнением дополнительных преобразований, загрузка таблицы в таблицу кучи гораздо быстрее, чем загрузка данных в кластеризованную таблицу columnstore. Кроме того, загрузка данных во временную таблицу выполняется быстрее, чем загрузка таблицы в постоянное хранилище. После загрузки данных можно создавать индексы в таблице для повышения производительности запросов.
Таблицы columnstore кластера начинают достичь оптимального сжатия после того, как существует более 60 миллионов строк. Для небольших поисковых таблиц (менее 60 млн строк) для повышения производительности запросов лучше использовать кучу или кластеризованный индекс.
Чтобы создать таблицу кучи, укажите HEAP в предложении WITH:
CREATE TABLE myTable
(
id int NOT NULL,
lastName varchar(20),
zipCode varchar(6)
)
WITH ( HEAP );
Примечание.
Если вы часто выполняете операции INSERT
, UPDATE
или DELETE
с таблицей кучи, рекомендуется включить перестроение таблицы в расписание обслуживания, используя команду ALTER TABLE
. Например, ALTER TABLE [SchemaName].[TableName] REBUILD
. Эта практика способствует снижению фрагментации, что приводит к повышению производительности во время операций чтения.
Кластеризованные и некластеризованные индексы
Кластеризованные индексы могут превосходить кластеризованные колоночные таблицы, когда требуется быстро извлечь отдельную строку. Для запросов, требующих быстрой выборки одной или очень небольшого количества строк, рекомендуется рассмотреть использование кластеризованного индекса или некластеризованного вторичного индекса. Недостаток использования кластеризованного индекса заключается в том, что эффективными будут только те запросы, в которых используется высокоизбирательный фильтр для столбца с кластеризованным индексом. Чтобы улучшить фильтр в других столбцах, добавьте в них некластеризованный индекс. Однако каждый добавленный в таблицу индекс увеличивает пространство и время обработки загрузки.
Чтобы создать кластеризованную таблицу индексов, укажите CLUSTERED INDEX в предложении WITH:
CREATE TABLE myTable
(
id int NOT NULL,
lastName varchar(20),
zipCode varchar(6)
)
WITH ( CLUSTERED INDEX (id) );
Чтобы добавить некластеризованный индекс в таблицу, используйте следующий синтаксис:
CREATE INDEX zipCodeIndex ON myTable (zipCode);
Оптимизация кластеризованных индексов колоночного хранилища
В кластеризованных таблицах columnstore данные упорядочены по сегментам. Качество сегментов существенно влияет на эффективность выполнения запросов в таблице Columnstore. Качество сегмента можно измерить по числу строк в сжатой группе строк. Качество сегментов наиболее оптимально, когда на каждую сжатую группу строк приходится как минимум 100 000 строк; производительность увеличивается по мере приближения количества строк в группе к 1 048 576 строкам, что является максимальным количеством строк, которое может содержать группа.
Для вычисления среднего количества строк для каждой группы строк и определения неоптимальных кластеризованных индексов Columnstore можно создать и использовать представление, подобное приведенному ниже. Последний столбец в этом представлении создает инструкцию SQL, которую можно использовать для перестроения индексов.
CREATE VIEW dbo.vColumnstoreDensity
AS
SELECT
GETDATE() AS [execution_date]
, DB_Name() AS [database_name]
, s.name AS [schema_name]
, t.name AS [table_name]
, MAX(p.partition_number) AS [table_partition_count]
, SUM(rg.[total_rows]) AS [row_count_total]
, SUM(rg.[total_rows])/COUNT(DISTINCT rg.[distribution_id]) AS [row_count_per_distribution_MAX]
, CEILING((SUM(rg.[total_rows])*1.0/COUNT(DISTINCT rg.[distribution_id]))/1048576) AS [rowgroup_per_distribution_MAX]
, SUM(CASE WHEN rg.[State] = 0 THEN 1 ELSE 0 END) AS [INVISIBLE_rowgroup_count]
, SUM(CASE WHEN rg.[State] = 0 THEN rg.[total_rows] ELSE 0 END) AS [INVISIBLE_rowgroup_rows]
, MIN(CASE WHEN rg.[State] = 0 THEN rg.[total_rows] ELSE NULL END) AS [INVISIBLE_rowgroup_rows_MIN]
, MAX(CASE WHEN rg.[State] = 0 THEN rg.[total_rows] ELSE NULL END) AS [INVISIBLE_rowgroup_rows_MAX]
, AVG(CASE WHEN rg.[State] = 0 THEN rg.[total_rows] ELSE NULL END) AS [INVISIBLE_rowgroup_rows_AVG]
, SUM(CASE WHEN rg.[State] = 1 THEN 1 ELSE 0 END) AS [OPEN_rowgroup_count]
, SUM(CASE WHEN rg.[State] = 1 THEN rg.[total_rows] ELSE 0 END) AS [OPEN_rowgroup_rows]
, MIN(CASE WHEN rg.[State] = 1 THEN rg.[total_rows] ELSE NULL END) AS [OPEN_rowgroup_rows_MIN]
, MAX(CASE WHEN rg.[State] = 1 THEN rg.[total_rows] ELSE NULL END) AS [OPEN_rowgroup_rows_MAX]
, AVG(CASE WHEN rg.[State] = 1 THEN rg.[total_rows] ELSE NULL END) AS [OPEN_rowgroup_rows_AVG]
, SUM(CASE WHEN rg.[State] = 2 THEN 1 ELSE 0 END) AS [CLOSED_rowgroup_count]
, SUM(CASE WHEN rg.[State] = 2 THEN rg.[total_rows] ELSE 0 END) AS [CLOSED_rowgroup_rows]
, MIN(CASE WHEN rg.[State] = 2 THEN rg.[total_rows] ELSE NULL END) AS [CLOSED_rowgroup_rows_MIN]
, MAX(CASE WHEN rg.[State] = 2 THEN rg.[total_rows] ELSE NULL END) AS [CLOSED_rowgroup_rows_MAX]
, AVG(CASE WHEN rg.[State] = 2 THEN rg.[total_rows] ELSE NULL END) AS [CLOSED_rowgroup_rows_AVG]
, SUM(CASE WHEN rg.[State] = 3 THEN 1 ELSE 0 END) AS [COMPRESSED_rowgroup_count]
, SUM(CASE WHEN rg.[State] = 3 THEN rg.[total_rows] ELSE 0 END) AS [COMPRESSED_rowgroup_rows]
, SUM(CASE WHEN rg.[State] = 3 THEN rg.[deleted_rows] ELSE 0 END) AS [COMPRESSED_rowgroup_rows_DELETED]
, MIN(CASE WHEN rg.[State] = 3 THEN rg.[total_rows] ELSE NULL END) AS [COMPRESSED_rowgroup_rows_MIN]
, MAX(CASE WHEN rg.[State] = 3 THEN rg.[total_rows] ELSE NULL END) AS [COMPRESSED_rowgroup_rows_MAX]
, AVG(CASE WHEN rg.[State] = 3 THEN rg.[total_rows] ELSE NULL END) AS [COMPRESSED_rowgroup_rows_AVG]
, 'ALTER INDEX ALL ON ' + s.name + '.' + t.NAME + ' REBUILD;' AS [Rebuild_Index_SQL]
FROM sys.[dm_pdw_nodes_db_column_store_row_group_physical_stats] rg
JOIN sys.[pdw_nodes_tables] nt ON rg.[object_id] = nt.[object_id]
AND rg.[pdw_node_id] = nt.[pdw_node_id]
AND rg.[distribution_id] = nt.[distribution_id]
JOIN sys.[pdw_permanent_table_mappings] mp ON nt.[name] = mp.[physical_name]
JOIN sys.[tables] t ON mp.[object_id] = t.[object_id]
JOIN sys.[schemas] s ON t.[schema_id] = s.[schema_id]
JOIN sys.[partitions] p ON P.object_id = t.object_id
GROUP BY
s.[name]
, t.[name];
Теперь, когда вы создали представление, выполните этот запрос, чтобы определить таблицы с группами строк с менее чем 100-K строками. Вы можете увеличить пороговое значение 100 К, если вы ищете более оптимальное качество сегмента.
SELECT *
FROM [dbo].[vColumnstoreDensity]
WHERE COMPRESSED_rowgroup_rows_AVG < 100000
OR INVISIBLE_rowgroup_rows_AVG < 100000;
После выполнения запроса можно начать просмотр данных и анализ результатов. В таблице ниже показано, на какие данные следует обратить внимание при анализе групп строк.
Столбец | Как использовать эти данные |
---|---|
[table_partition_count] | Если таблица секционирована, можно ожидать увеличения числа открытых групп строк. Каждая секция в распределении теоретически может иметь связанную с ней открытую группу строк. Это следует учитывать при анализе. Небольшую секционированную таблицу можно оптимизировать, полностью отменив секционирование, так как это улучшит сжатие. |
[row_count_total] | Общее количество строк в таблице. Например, это значение можно использовать для вычисления процента строк в сжатом состоянии. |
[количество_строк_на_распределение_МАКС] | Если все строки распределены равномерно, это значение будет целевым числом строк для каждого распределения. Сравните это значение с compressed_rowgroup_count. |
[COMPRESSED_rowgroup_rows] | Общее количество строк в формате columnstore для таблицы. |
[COMPRESSED_rowgroup_rows_AVG] | Если среднее количество строк существенно меньше максимального числа строк для группы строк, возможно, стоит использовать инструкции CTAS или ALTER INDEX REBUILD для повторного сжатия данных. |
[СЖАТОЕ_количество_групп_строк] | Число групп строк в формате columnstore. Если это число намного больше по сравнению с таблицей, это указывает на низкую плотность columnstore. |
[COMPRESSED_rowgroup_rows_DELETED] | Логически удаленные строки хранятся в формате columnstore. Если это число велико относительно размера таблицы, возможно, стоит заново создать раздел или перестроить индекс, так как это приводит к их физическому удалению. |
[COMPRESSED_rowgroup_rows_MIN] | Используйте это вместе со столбцами AVG и MAX, чтобы понять диапазон значений для групп строк в вашем хранилище столбцов. Небольшое значение относительно порогового значения загрузки (102 400 для распределения с выравниванием по секциям) говорит о том, что загрузку данных можно оптимизировать. |
[COMPRESSED_rowgroup_rows_MAX] | То же, что и выше |
[OPEN_rowgroup_count] | Открытые группы строк считаются нормой. Ожидается наличие одной открытой группы строк на каждое распределение таблиц (60). Очень большие числа говорят о загрузке данных в разные разделы. Перепроверьте стратегию секционирования, чтобы убедиться в ее надежности. |
[OPEN_rowgroup_rows] | Каждая группа строк может иметь максимум 1 048 576 строк. Используйте это значение для оценки текущей наполненности открытых групп строк. |
[OPEN_rowgroup_rows_MIN] | Наличие открытых групп указывает, что либо данные медленно загружаются в таблицу, либо предыдущая загрузка привела к переносу оставшихся строк в эту группу строк. С помощью столбцов MIN, MAX, AVG можно узнать, сколько данных находится в открытых группах строк. Для небольших таблиц это может быть 100 % всех данных! В этом случае следует использовать инструкцию ALTER INDEX REBUILD, чтобы принудительно перенести данные в columnstore. |
[OPEN_rowgroup_rows_MAX] | То же, что и выше |
[OPEN_rowgroup_rows_AVG] | То же, что и выше |
[CLOSED_rowgroup_rows] | Просмотрите строки закрытой группы строк в качестве проверки. |
[CLOSED_rowgroup_count] | Число закрытых групп строк должно быть небольшим (если они вообще присутствуют). Закрытые группы строк можно преобразовать в сжатые группы строк с помощью команды ALTER INDEX ... REORGANIZE. Однако обычно это не требуется. Автоматически закрытые группы преобразуются во фоновом процессе "tuple mover" в группы строк Columnstore. |
[ЗАКРЫТО_rowgroup_rows_МИН] | Закрытые группы строк должны иметь очень высокий коэффициент заполнения. Если коэффициент заполнения для закрытой группы строк низок, то требуется дальнейший анализ Columnstore. |
[CLOSED_rowgroup_rows_MAX] | То же, что и выше |
[CLOSED_rowgroup_rows_AVG] | То же, что и выше |
[Rebuild_Index_SQL] | SQL для перестроения колонночного индекса для таблицы |
Влияние обслуживания индекса
Столбец Rebuild_Index_SQL
в представлении vColumnstoreDensity
содержит инструкцию ALTER INDEX REBUILD
, которую можно использовать для перестроения индексов. При перестроении индексов убедитесь, что вы выделили достаточный объем памяти для сеанса, который выполняет перестроение вашего индекса. Для этого повысьте класс ресурсов для пользователя, который имеет разрешение на перестроение индекса для этой таблицы, до рекомендованного минимального класса. Пример см. в разделе Перестроение индексов для улучшения качества сегментов далее в этой статье.
Для таблицы с упорядоченным кластеризованным индексом columnstore ALTER INDEX REBUILD
будет выполнять повторную сортировку данных с помощью tempdb. Контролируйте tempdb во время операций перестроения. Если вам необходимо больше места в базе данных tempdb, можно увеличить объем хранилища данных. Уменьшите масштаб после перестроения индекса.
Для таблицы с упорядоченным кластеризованным columnstore индексом ALTER INDEX REORGANIZE
данные не пересортировываются. Для повторной сортировки данных используйте ALTER INDEX REBUILD
.
Дополнительные сведения см. в статье Настройка производительности с помощью упорядоченного кластеризованного индекса columnstore.
Причины низкого качества индекса Columnstore
Если вы обнаружили таблицы с сегментами низкого качества, необходимо определить причину. Ниже описаны распространенные причины низкого качества сегментов:
- Нехватка памяти при создании индекса.
- Высокая интенсивность операций DML.
- Небольшой или минимальный объем операций загрузки
- Слишком много разделов
Эти факторы могут привести к тому, что индекс columnstore будет содержать значительно меньше оптимального количества — 1 миллиона строк на группу строк. Кроме того, строки могут попасть в разностную группу строк вместо сжатой группы строк.
Примечание.
Таблицы колонночного хранилища обычно не отправляют данные в сжатый сегмент колонночного хранилища, пока не будет более 1 миллиона строк на таблицу. Если таблица с кластеризованным индексом columnstore содержит множество открытых групп строк с общим числом строк, которые не соответствуют пороговой величине сжатия (1 миллиона строк), эти группы строк останутся открытыми и будут храниться в виде данных строк. Следовательно, это приведет к увеличению размера базы данных распространителя, так как они не сжимаются. Кроме того, эти открытые группы строк не будут использовать CCI и потребуют больше ресурсов для обслуживания. Возможно, рекомендуется использовать ALTER INDEX REORGANIZE.
Давление на память при построении индекса.
Количество строк в сжатой группе строк непосредственно зависит от ширины строк и объема памяти, доступного для обработки группы строк. Если во время записи строк в таблицы Columnstore возникает нехватка памяти, качество сегмента Columnstore может ухудшиться. Поэтому лучшим подходом является предоставление сессии, записывающей данные в ваши таблицы с индексами Columnstore, доступа к максимально возможному объему памяти. Так как существует компромисс между памятью и параллелизмом, рекомендации по правильному выделению памяти зависят от данных в каждой строке таблицы, единиц хранилища данных, выделенных вашей системе, и количества слотов параллелизма, которые можно предоставить сеансу, который записывает данные в таблицу.
Высокая интенсивность операций DML.
Интенсивные операции DML по обновлению и удалению строк приводят к неэффективной работе Columnstore. Это особенно применимо в случае изменения большей части строк в группе строк.
- Если строка удаляется из сжатой группы строк, она лишь логически помечается как удаленная. Строка остается в сжатой группе строк до тех пор, пока секция или таблица не будут перестроены.
- При вставке строка добавляется во внутреннюю таблицу хранилища строк, называемую разностной строковой группой. Вставленная строка не преобразуется в колоночное хранилище данных, пока дельта группа строк не будет заполнена и помечена как закрытая. Группы строк закрываются после достижения максимального объема в 1 048 576 строк.
- Обновление строки в формате columnstore обрабатывается как логическое удаление, а затем — как вставка. Вставленную строку можно сохранить в разностном хранилище.
Пакетные операции обновления и вставки, превышающие пороговое значение в 102 400 строк для распределения с выравниванием по секциям, записываются непосредственно в формате columnstore. Чтобы это произошло, при условии равномерного распределения потребуется изменить более 6,144 млн строк в рамках одной операции. Если количество строк для распределения, выровненного по секциям, не превышает 102 400, строки отправляются в дельта-хранилище и остаются там до тех пор, пока не будет вставлено или изменено достаточное количество строк для закрытия группы строк или не будет перестроен индекс.
Небольшой объем операций загрузки или медленный непрерывный поток загрузки
Небольшие загрузки, поступающие в выделенный пул SQL, иногда называют потоковыми. Обычно они представляют собой практически постоянный поток данных, принимаемых системой. Однако, так как этот поток почти непрерывный объем строк не особенно велик. Чаще всего количество данных значительно ниже порогового значения, необходимого для непосредственной загрузки в формате columnstore.
В таких ситуациях часто лучше сначала загружать данные в хранилище BLOB-объектов Azure и дать им накопиться, прежде чем загружать. Этот метод часто называют микропакетной обработкой.
Слишком много разделов
Еще одним фактором, который следует учитывать, является влияние секционирования на ваши кластеризованные колоночныеstore таблицы. Перед секционированием выделенный пул SQL уже разделяет ваши данные на 60 частей. Во время секционирования выполняется дальнейшее разделение данных. Чтобы воспользоваться преимуществами кластеризованного индекса columnstore при секционировании данных, каждая секция должна содержать по крайней мере 1 миллион строк. Если таблица состоит из 100 секций, то чтобы использовать кластеризованный индекс columnstore, она должна состоять из минимум 6 миллиардов строк (60 распределений * 100 секций * 1 миллион строк). Если в вашей таблице из 100 секций нет 6 миллиардов строк, уменьшите количество секций или попробуйте использовать табличную кучу.
Как только ваши таблицы будут загружены данными, выполните следующие шаги, чтобы выявить и перестроить таблицы с неоптимальными кластеризованными колонностор индексами.
Повышение качества сегментов за счет перестроения индексов
Шаг 1. Определение или создание пользователя, который использует соответствующий класс ресурсов
Простой способ быстро повысить качество сегментов — перестроить индекс. Инструкция SQL, возвращенная в предыдущем представлении, содержит инструкцию ALTER INDEX REBUILD, которую можно использовать для перестроения индексов. При перестроении ваших индексов убедитесь, что выделен достаточный объем памяти для сеанса, который перестраивает ваш индекс. Для этого повысьте класс ресурсов для пользователя, который имеет разрешение на перестроение индекса для этой таблицы, до рекомендованного минимального класса.
Ниже приведен пример того, как можно выделить дополнительный объем памяти для пользователя, увеличив класс ресурсов. Сведения об использовании классов ресурсов см. в статье Классы ресурсов для управления рабочими нагрузками.
EXEC sp_addrolemember 'xlargerc', 'LoadUser';
Шаг 2. Перестройка кластеризованных индексов Columnstore, используя пользователя с более высоким классом ресурсов
Войдите в систему от имени пользователя из шага 1 (LoadUser
), которому теперь соответствует более высокий класс ресурсов, и выполните инструкции ALTER INDEX. Убедитесь, что этот пользователь имеет разрешение ALTER в отношении таблиц, в которых будет выполнятся перестроение индекса. В этих примерах показано перестроение всего индекса Columnstore и перестроение одной секции. В больших таблицах более удобно перестраивать индексы по одному разделу за раз.
Кроме того, вместо перестроения индекса можно копировать таблицу в новую таблицу, используя инструкцию CTAS. Какой способ лучше? Для больших объемов данных инструкция CTAS обычно выполняется быстрее, чем ALTER INDEX. Что же касается небольших объемов данных, рекомендуем использовать инструкцию ALTER INDEX. Она проще в использовании и не требует замены таблицы.
-- Rebuild the entire clustered index
ALTER INDEX ALL ON [dbo].[DimProduct] REBUILD;
-- Rebuild a single partition
ALTER INDEX ALL ON [dbo].[FactInternetSales] REBUILD Partition = 5;
-- Rebuild a single partition with archival compression
ALTER INDEX ALL ON [dbo].[FactInternetSales] REBUILD Partition = 5 WITH (DATA_COMPRESSION = COLUMNSTORE_ARCHIVE);
-- Rebuild a single partition with columnstore compression
ALTER INDEX ALL ON [dbo].[FactInternetSales] REBUILD Partition = 5 WITH (DATA_COMPRESSION = COLUMNSTORE);
В выделенном пуле SQL операция перестроения индекса выполняется в автономном режиме. Дополнительные сведения о перестройке индексов см. в разделе об использовании инструкции ALTER INDEX REBUILD в статьях Дефрагментация колонночных индексов и ALTER INDEX.
Шаг 3: Убедитесь, что качество сегментов кластеризованного колонночного хранилища улучшилось.
Повторно выполните запрос на определение таблицы с сегментами низкого качества и убедитесь, что качество сегментов улучшилось. Если качество сегмента не улучшилось, дело может быть в том, что строки в вашей таблице слишком широкими. Рассмотрите возможность использования более высокой категории ресурсов или DWU при перестроении индексов.
Перестроение индексов с помощью инструкции CTAS и переключения секций
В этом примере для перестроения секции таблицы используется инструкция CREATE TABLE AS SELECT (CTAS) и переключение секций.
-- Step 1: Select the partition of data and write it out to a new table using CTAS
CREATE TABLE [dbo].[FactInternetSales_20000101_20010101]
WITH ( DISTRIBUTION = HASH([ProductKey])
, CLUSTERED COLUMNSTORE INDEX
, PARTITION ( [OrderDateKey] RANGE RIGHT FOR VALUES
(20000101,20010101
)
)
)
AS
SELECT *
FROM [dbo].[FactInternetSales]
WHERE [OrderDateKey] >= 20000101
AND [OrderDateKey] < 20010101
;
-- Step 2: Switch IN the rebuilt data with TRUNCATE_TARGET option
ALTER TABLE [dbo].[FactInternetSales_20000101_20010101] SWITCH PARTITION 2 TO [dbo].[FactInternetSales] PARTITION 2 WITH (TRUNCATE_TARGET = ON);
Дополнительные сведения о перестроении секций с помощью CTAS см. в статье Секционирование таблиц в выделенном пуле SQL.
Связанный контент
Дополнительные сведения о разработке таблиц см. в разделе Разработка таблиц.