суббота, 12 января 2019 г.

Тонкая настройка MySQL

Тонкая настройка MySQL


Эта заметка, в основном, является переводом соответствующих разделов руководства по MySQL.
Здесь собраны возможные способы настройки и использования MySQL-сервера для достижения максимальной производительности. Все нижесказанное относится к версии 3.22 для Linux. Для других операционных систем некоторые из утверждений могут не выполняться.

Содержание:

    Компилирование исходников
    Настройка переменных
    Как MySQL работает с памятью
    Как работать с таблицами для достижения большей производительности
    Форматы таблиц в MySQL
    Использование индексов
    Общие рекомендации по повышению производительности

Компилирование исходников

Для достижения наибольшей производительности сервера нужно учитывать такие факты:
  1. При компиляции pgcc с опцией -O6 mysqld работает на 11 % быстрее, чем если компилировать обычным gcc.
  2. Если использовать динамическую линковку, то результат будет на 13% медленней, чем при статической.
  3. Если использовать TCP/IP соединения, то результат на 7.5% хуже, чем при использовании UNIX - сокетов.
В связи с этим рекомендуется поставить компилятор pgcc ( http://www.goof.com/pcg/ ). Этот совет, конечно, имеет смысл, если у вас Pentium процессор. Pgcc будет полезен не только для компилирования MySQL, разработчики этого компилятора утверждают, что откомпилированные им программы минимум на 5% работают быстрее, чем откомпилированные с помощью gcc.
Запуск конфигуратора может иметь такой вид ( во внимание вышеизложенные факты ):
CFLAGS="-O6 -fomit-frame-pointer" \
CXX=gcc \
CXXFLAGS="-O6 -fomit-frame-pointer \
-felide-constructors -fno-exceptions -fno-rtti" \
./configure \
--enable-assembler \
--disable-shared \
--with-mysqld-ldflags="-all-static" \
--with-client-ldflags="-all-static" \
--with-unix-socket-path=/tmp/mysql.sock \
--prefix=/usr

Настройка переменных

Если запустить mysqladmin -variables, то можно увидеть примерно такую картину.
Variable_nameValue
back_log5
connect_timeout5
basedir/usr/local/
datadir/home/www/data/
delayed_insert_limit100
delayed_insert_timeout300
delayed_queue_size1000
join_buffer131072
flush_time0
key_buffer8388600
language/usr/local/share/mysql/english/
logOFF
log_updateOFF
long_query_time10
low_priority_updatesOFF
max_allowed_packet1048576
max_connections100
max_connect_errors10
max_delayed_insert_threads20
max_join_size4294967295
max_sort_length1024
net_buffer_length16384
pid_file/usr/local/var/mysqld.pid
port3306
protocol_version10
record_buffer131072
skip_lockingON
skip_networkingOFF
socket/tmp/mysql.sock
sort_buffer2097144
table_cache64
thread_stack65536
tmp_table_size1048576
tmpdir/tmp/
version3.22.27
wait_timeout28800
От настройки этих переменных сильно зависит производительность сервера. Надо сказать, что не существует наилучших значений для всех случаев. Для каждого конкретного сервера и поставленных задач нужно выбирать свои значения.
Рассмотрим подробнее эти переменные (......выделены наиболее важные из них ).
back_log - этот параметр показывает, сколько одновременно может быть невыполненных запросов на соединение (connection requests). Параметр имеет большое значение в тех случаях, когда к MySQL поступает ОЧЕНЬ много запросов на соединение в малый промежуток времени.
Когда к MySQL поступает connect-запрос, производятся следующие действия - проверяется, разрешен ли доступ к серверу, и если разрешен, то порождается новый процесс. Все это занимает достаточно мало времени. Однако если за это время поступит еще один connect-запрос, то он заносится в очередь. Параметр back_log определяет длину этой очереди. Если количество запросов превысит данное значение, то все непомещающиеся запросы будут игнорироваться. По умолчанию значение back_log равно 5, что вполне достаточно для большинства серверов.
Максимально значение back_log ограничено операционной системой.
connect_timeout - количество секунд, которое сервер ждет connect-пакета, по истечении этого времени будет выдан пакет " соединение ". Для более детальных разъяснений см. описание TCP/IP протокола.
delayed_insert_timeout - как долго поток INSERT DELAYED будет ожидать данных для INSERT. Более подробно значение слова DELAYED расписано в описании INSERT запроса.
delayed_insert_limit - INSERT DELAYED вставив количество записей, равное delayed_insert_limit, проверяет, есть ли SELECT-запрос к этой же таблице. Если есть, то выполняется SELECT, и только после этого продолжается INSERT.
delayed_queue_size - Для выполнения INSERT DELAYED будет выделятся очередь длиной в delayed_queue_size строк. Когда очередь заполниться все остальные конкурирующие INSERT DELAYED запросы будут ждать, пока не освободиться место в этой очереди.
flush_time - Если значение больше нуля, то каждые flush_time секунд все таблицы будут закрываться. Это позволит освобождать неиспользуемые ресурсы и синхронизировать данные на диске.
join_buffer - Величина буфера, который используется для полных JOIN запросов (.. для полного объединения двух таблиц без использования индексов ). Память под такой буфер выделяется один раз для каждого запроса. Увеличение параметра ускорит выполнение таких запросов. Более естественный путь ускорить полные JOIN запросы - использовать индексы.
key_buffer - Величина буфера ( байтах ), который используется для индексов. Этот буфер общий для всех потоков. Если используется много DELETE или INSERT запросов к таблицам с большим кол - индексов, то увеличение значения повысит скорость выполнения таких запросов. Для достижения еще большей скорости нужно использовать LOCK TABLES.
long_query_time - Если время выполнения запроса превысит данное значение ( сек.), то внутренний счетчик slow_queries будет увеличен на 1. Посмотреть значение счетчика можно командой mysql>status.
max_allowed_packet - Максимальный размер пакета для передачи данных. Данные между клиентом и сервером передаются пакетами. В начале создается пакет длиной net_buffer_length затем, если размер данных больше, то размер пакета увеличивается до необходимого значения, при этом его длина не может превысить значение max_allowed_packet. Если используются поля BLOB большого размера, то рекомендуется увеличить значение этого параметра. В идеале нужно присвоить этой переменной значение размера самого большого BLOB поля.
max_connections - Максимальное количество открытых соединений. Определяет, сколько клиентов одновременно могут работать с сервером. Увеличение параметра увеличивает количество используемых дескрипторов файла.
max_connect_errors - Если в процессе общения с клиентом произошел обрыв соединения (interrupt connection), то счетчик ошибок для хоста клиента увеличивается на 1. Когда значение этого счетчика достигнет max_connect_errors, то все последующие соединения с данного хоста будут игнорироваться. Для обнуления счетчиков использовать команду FLUSH HOSTS.
max_delayed_threads - Максимальное количество потоков, которые выполняют INSERT DELAYED. Если будет вызван запрос INSERT DELAYED, а при этом достигнуто значение max_delayed_threads, то такой запрос будет выполнен как обычный INSERT ( опции DELAYED).
max_join_size - Максимальное количество записей, которое может быть возвращено полным JOIN запросом. Если в JOIN запросе кол - записей превысит это значение, то будет возвращена ошибка. Увеличение значения этого параметра позволит выполнять большие запросы, но при этом следует учитывать, то такие запросы съедают много процессорного времени и могут содержать миллионы записей.
max_sort_length - При сортировке BLOB или TEXT полей из каждого поля берутся только первые max_sort_length байт, а остальные отбрасываются и при сортировке не учитываются.
max_tmp_tables - Максимальное количество временных таблиц, которые клиент может сохранять открытыми одновременно. На самом деле в версии 3.22 это поле ни на что не влияет.
net_buffer_length - Размер пакета для передачи данных (. max_allowed_packet) Обычно этот параметр не нужно изменять, но если у вас очень мало памяти, то его можно уменьшить до ожидаемого размера результата запроса.
record_buffer - Каждый поток, который осуществляет последовательное сканирование таблиц ( это происходит в SELECT запросах ), для каждой таблицы, участвующей в сканировании выделяет память размером record_buffer. Если будет много запросов, требующих последовательно сканировать таблицы, то значение этого параметра рекомендуется увеличить.
sort_buffer - Каждый поток, который осуществляет сортировку данных (ORDER BY или GROUP BY), выделяет память размером sort_buffer. Для повышения быстродействия запросов с ORDER BY или GROUP BY это значение необходимо увеличить.
table_cache - Количество открытых таблиц для ВСЕХ потоков. Увеличение значения приведет к увеличению количества используемых дескрипторов файла. MySQL необходимо 2 дескриптора для каждой открытой таблицы.
tmp_table_size - Максимальный размер временных таблиц. При превышении этого размера возвращается ошибка table tbl_name is full. При использовании сложных GROUP BY запросов значение нужно увеличить.
thread_stack - Размер стека для каждого потока. Обычно значение по умолчанию является достаточным.
wait_timeout - Время, которое поток ждет повторного обращения. Если за это время к потоку не было ни одного обращения, то поток убивается.
Параметры table_cache, max_connections и max_tmp_tables определяют, как много файлов сервер будет держать открытыми. Максимально количество открытых файлов для каждого процесса ограничивается операционной системой. На многих ОП это количество можно увеличить. Для более детальной информации см. руководство по вашей ОП.
table_cache зависит от max_connections. Например, если у вас 200 открытых соединений, то вам может понадобиться до 200*n открытых таблиц, где n - количество таблиц, участвующих в запросах.
MySQL при работе с таблицами использует хорошо масштабируемые алгоритмы, так что MySQL может работать даже при малых объемах памяти. Естественно для лучшей производительности нужно больше оперативной памяти.
Изменить значение настроек можно при запуске сервера опцией -O, например,
>safe_mysqld -O key_buffer=1 6M
(значениях, которые измеряются в байтах, для сокращения, можно использовать буквы К и М ).
Если у вас много памяти и много таблиц, то для увеличения производительности, при запуске сервера можно использовать такие значения.
>safe_mysqld -O key_buffer=16M -O table_cache=128 \
                -O sort_buffer=4M -O record_buffer=1M &
Если у вас мало памяти и ожидается мало соединений, то лучше сервер запускать с такими опциями
>safe_mysqld -O key_buffer=512k -O sort_buffer=100k \
          -O record_buffer=100k &
или даже
> safe_mysqld -O key_buffer=512k -O sort_buffer=16k \
          -O table_cache=32 -O record_buffer=8k -O net_buffer=1K &
Необходимо принимать во внимание, что, если сервер сконфигурировать для использования малого объема памяти, то в случае большого числа соединений может возникнуть " своппинга ", которая сильно затормозит сервер.

Как MySQL работает с памятью.

Здесь приведено описание того, как MySQL сервер работает с оперативной памятью. В скобочках жирными буквами указаны переменные, которые влияют на то или иное значение.
  1. Key_buffer является общим для всех потоков. Все остальные буфера выделяются по мере необходимости.
  2. Каждое соединение использует некоторое количество памяти. Это память для стека (thread_stack), буфер соединения (net_buffer_length) и буфер результата (net_buffer_length)
  3. Для каждого запроса, требующего последовательно сканировать таблицу, выделяется буфер чтения (record_buffer).
  4. Все JOIN запросы выполняются в один проход и для большинства таких запросов нет необходимости применять вспомогательные таблицы. В основном вспомогательные таблицы формируются в памяти, но если такая таблица имеет слишком большой размер записи или используется тип BLOB, то она сохраняется на диске. Если размер вспомогательной таблицы, которая хранится в памяти, превысит tmp_table_size, то будет возвращена ошибка table_name is full. Для избежания такой ошибки нужно или увеличить значение tmp_table_size, или включить опцию SQL_BIG_TABLES ( можно сделать либо запросом SET SQL_BIG_TABLES=1, либо запускать mysqld с опцией -big-tables). При включенной опции SQL_BIG_TABLES все вспомогательные таблицы формируются не в памяти, а на диске.
  5. Запросы с сортировкой выделяют в памяти буфер для сортировки (sort_buffer) и используют один или два временных файла.
  6. Таблицы с данными открываются каждым конкурирующим потоком. Индексные файлы открываются всего один раз, не зависимо от количества потоков, использующих эти файлы.
  7. Для таблиц с BLOB-полями буфер автоматически увеличивается до размера самого большого BLOB-поля.
  8. Дескрипторы всех открытых таблиц хранятся в кэше, который работает по принципу FIFO. Размер кэша определяется переменной table_cache. Если несколько потоков открывают одну и туже таблицу, то для каждого потока выделяется свой дескриптор таблицы.
  9. Команда mysqladmin flash-tables закрывает все таблицы, которые не используются в данный момент, а все используемые таблицы помечает для закрытия. Такая операция позволяет освободить неиспользуемую память.

Как работать с таблицами для достижения большей производительности.

  1. По возможности все поля декларировать как NOT NULL. Это сделает работу с таблицами более быстрой и сохранит 1 бит на каждое такое поле.
  2. Применять значения по умолчанию (DEFAULT). При вызове запроса INSERT в таблицу будут записываться только те поля, значения которых отличаются от DEFAULT.
  3. Используйте настолько малые типы INT, насколько это возможно. Например, применять MEDIUMINT намного лучше, чем обычный INT.
  4. Если у вас нет записей с переменной длиной ( ни одного поля с типом VARCHAR, BLOB или TEXT), то таблица сохраняется в формате " постоянной длиной записи ". Это несколько расходует память, но намного повышает скорость работы.
  5. При использовании нескольких последовательных INSERT запросов, лучше все данные указать в одном INSERT, чем делать несколько INSERT.
  6. При загрузке данных в таблицу лучше использовать LOAD DATA INFILE, чем INSERT, такой метод в 20 раз быстрее.
  7. Для увеличения скорости LOAD DATA INFILE и INSERT нужно увеличить значение переменной key_buffer.
  8. Если ожидается много запросов INSERT или UPDATE, работающих одновременно, то для большей скорости рекомендуется приметь LOCK TABLES.
  9. Время от времени нужно дефрагметировать таблицы. Это делается утилитой isamchk с опциями - evi.

Форматы таблиц в MySQL

MySQL для хранения данных использует три типа таблиц: с фиксированной длиной строки, с динамической длиной строки и сжатые таблицы.
Таблицы с фиксированной длиной строки.
    Этот формат применяется по умолчанию, если в таблице нет полей с типом VARCHAR,
    BLOB или TEXT.
    Все поля типа CHAR, NUMERIC и DECIMAL дополняются в конце пробелами.
    Высокая скорость работы.
    Легко кэшируются.
    Легко восстановить после краха, так как все строки имеют постоянную длину.
    Не требуют реорганизации ( помощью isamchk), до тех пор, пока не будет удалено очень много записей, и вы захотите освободить место на диске.
    Обычно такие таблицы занимают больше места, чем таблицы с динамической длиной строки.
Таблицы с динамической длиной строки.
    Этот формат применяется, если в таблице есть поля с типом VARCHAR, BLOB или TEXT.
    Все строки динамические (CHAR хранятся как VARCHAR, кроме тех у которых длина меньше 4).
    Каждое поле имеет дополнительный бит, который устанавливается, если строковое поле равно "" ( строка ), или если числовое поле равно 0 ( не то же самое, когда поле может иметь значение NULL).
    Непустые строки хранятся в виде {_} {_}
    Обычно такие таблицы занимают намного меньше места, чем таблицы с фиксированной длиной.
    Ожидаемая длина строки вычисляется по формуле: 3+(количество полей + 7)/8+(количество полей типа CHAR)+(размер числовых типов в бинарном виде )+(длина всех строк )+(количество NULL-полей + 7)/8.
Сжатые таблицы.
    Таблицы "только для чтения", их можно получить с помощью утилиты pack_isam. Эту утилиту получают все покупатели, которые приобрели расширенную поддержку MySQL.
    Основная характеристика - занимают мало места.

Использование индексов.

Все индексы (PRIMARY, UNIQUE и INDEX) хранятся в B-дереве. В строковых типах автоматически происходит сжатие начальных и конечных пробелов.
Индексы используются для:
    Быстрого поиска записей по условию WHERE;
    Для объединения таблиц с посредством JOIN;
    Поиска MAX() и MIN() значений для ключевых полей ;
    Для сортировки и группировки таблиц (......ORDER BY и GROUP BY);
    Для извлечения данных не из таблицы с данными, а из индексного файла. Это возможно только в некоторых случаях, например, когда все извлекаемые поля проиндексированы.
Рассмотрим следующий запрос SELECT:
SELECT * FROM tbl_name WHERE col =val AND col2=val2;
Если таблица имеет множественный индекс (col,col2), то соответствующие записи будут выбраны напрямую. Если существуют только одиночные индексы для col и col2, то оптимизатор сначала решит, при использовании какого индекса, количество возвращаемых записей будет меньше, а затем из этих записей будет произведена выборка по другому условию.
Если таблица имеет множественный индекс, то любой " префикс " этого индекса может использоваться для оптимизации запроса. Например, если есть индекс (col, col2, col3), то можно считать, что существуют индексы (col ); (col,col2); (col,col2,col3).
Любая другая часть индекса не может быть использована для оптимизации. Рассмотрим для примера такие запросы:
mysql> SELECT * FROM tbl_name WHERE col =val ;
mysql> SELECT * FROM tbl_name WHERE col2=val2;
mysql> SELECT * FROM tbl_name WHERE col2=val2 AND col3=val3;
Если есть индекс (col,col2,col3), то только в первом запросе будет использоваться индекс. Хотя второй и третий запросы содержат столбцы, которые присутствуют в индексе, но (col2) и (col2,col3) не являются левыми частями множественного индекса, и поэтому при выполнении этих запросов индекс применятся не будет.
MySQL также использует индексы для LIKE операций, если аргумент LIKE является строковой константой и при этом не начинается с символа шаблона (% или _). Например, следующие SELECT запросы используют индекс для key_col:
mysql> select * from tbl_name where key_col LIKE "Patrick%";
mysql> select * from tbl_name where key_col LIKE "Pat%_ck%";
А следующие два запроса выполняются без использования индекса:
mysql> select * from tbl_name where key_col LIKE "%Patrick%";
mysql> select * from tbl_name where key_col LIKE other_col;
В первом из этих запросов аргумент после LIKE начинается c символа шаблона, а во втором аргумент не является константой.

Общие рекомендации по повышению производительности.

  1. Запускать mysqld с правильно подобранными опциями (. настройка переменных ).
  2. Для ускорения SELECT запросов построить индексы для тех полей, которые участвуют в условии WHERE.
  3. Оптимизировать типы полей. По возможности использовать NOT NULL. (. работу с таблицами ).
  4. В MySQL применяется два способа блокировки таблиц (lock table) - внутренняя и внешняя блокировки. Внутренняя блокировка позволяет делать операции по изменению / данных атомарными ( конфликтующими с другими пользователями ). Внешняя блокировка применяется для одновременного доступа нескольких MySQL серверов к одним и тем же базам данных, а также внешняя блокировка позволяет запускать isamchk без остановки MySQL. Чтобы запретить использование внешней блокировки нужно запускать mysqld с опцией -skip-locking. Запрет внешней блокировки существенно повысит скорость работы, но при этом перед запуском isamchk нужно предварительно сбросить все данные на диск командой mysqladmin flush-tables. Также при запрете внешней блокировки нельзя будет использовать несколько серверов для работы с теми же базами данных. Задание прав доступа на конкретную таблицу или поле снижает производительность.

Во второй части будет описана работа с блокировкой таблиц при изменении и добавлении данных и как это влияет на скорость работы.

SQL справочник

SQL справочник

Скачать книгу SQL справочник
Книги по SQL — SQL справочник
SQL справочник
SQL справочник. Описание книги по SQL
Книга SQL Справочник является справочником по SQL стандарта ANSI SQL2003 и реализациям этого стандарта на наиболее распространенных платформах: Microsoft SQL Server, DB2 Universal Database версии 8.0 от компании ЮМ для Linux, Unix и Windows, Sybase Adaptive Server версии 12.5, Oracle Database 10g и двух продуктов с открытым исходным кодом MySQL версии 4 и PostgreSQL версии 7.
Книга SQL Справочник является настольным практическим справочником для администраторов и разработчиков баз данных, а также разработчиков прикладного программного обеспечения.

Скачать книгу SQL Справочник

Автор: Кевин Клайн.
Формат: Djvu.
Размер: 7.48 Mb.

Команды DML

Язык SQL. Формирование запросов к базе данных


SQL - этом мощный и в то же время не сложный язык для управления базами данных. Он поддерживается практически всеми современными базами данных. SQL подразделятся на два подмножества команд: DDL (Data Definition Language - язык определения данных) и DML (Data Manipulation Language - язык обработки данных). Команды DDL используются для создания новых баз данных, таблиц и столбцов, а команды DML - для чтения, записи, сортировки, фильтрования, удаления данных.Structured Query Language (Язык Структурированных Запросов) разработан корпораций IBM в начале 1970-х годов. В 1986 году SQL был впервые стандартизирован организаций ANSI.
Здесь будут рассмотрены подробно лишь команды DML, поскольку их приходится использовать гораздо чаще, чем команды DDL, то есть дается просто понятие о SQL.

О командах DDL

CREATE - используется для создания новых таблиц, столбцов и индексов.
DROP - используется для удаления столбцов и индексов.
ALTER - используется для добавления в таблицы новых столбцов и изменения определенных столбцов.

Команды DML

SELECT - наиболее часто используемая команда, применяется для получения набора данных из таблицы базы данных. Команда SELECT имеет следующий синтаксис:
SELECT список_полей1 FROM имя_таблицы [WHERE критерий ORDER BY список_полей2 [ASC | DESC]]
Операторы, находящие внутри квадратных скобок не обязательны, а вертикальная черта означает, что должна присутствовать одна из указанных фраз, но не обе.
Для примера создадим простейший запрос на получение данных из полей "name" и "phone" таблицы "friends":
SELECT name, phone FROM friends
Если необходимо получить все поля таблицы, то не обязательно их перечислять, достаточно поставить звездочку (*):
SELECT * FROM friends
Для исключения из выводимого списка повторяющихся записей, используется ключевое слово DISTINCT:
SELECT DISTINCT name FROM friends
Если необходимо получить отдельную запись, то используется оператор WHERE. Например, нам надо получить из таблицы "friends" номер телефона "Сергей Иванов":
SELECT * FROM friends WHERE name = ' Сергей Иванов'
или наоборот, нам надо узнать кому принадлежит телефон 293-89-13:
SELECT * FROM friends WHERE phone = 293-89-13'
Помимо этого можно использовать подстановочные символы, таким образом, создавая шаблоны поиска. Для этого используется оператор LIKE. Оператор LIKE имеет следующие операторы подстановки:
* - соответствует строке состоящей из одного или более символов;
_ - соответствует одному любому символу;
[] - соответствует одному символу из определенного набора;
Например, для получения записей из поля "name" содержащих слово "Сергей", запрос будит выглядеть следующим образом:
SELECT * FROM friends WHERE name LIKE '*Сергей*'
Для определения порядка, в котором возвращаются данные, используется оператор ORDER BY. Без этого оператора порядок возвращаемых данных невозможно предсказать. Ключевые слова ASC и DESC позволяют определить направление сортировки. ASC - упорядочивает по возрастанию, а DESC - по убыванию.
Например, запрос на получение списка записей из поля "name" в алфавитном порядке будет выглядеть следующим образом:
SELECT * FROM friends ORDER BY name
Обратим внимание на то, что ключевое слово ASC указывать не обязательно, поскольку оно используется по умолчанию.
INSERT - данная команда служит для добавления новой записи в таблицу. Записывается она следующим образом:
INSERT INTO имя_таблицы VALUES (список_значений)
Обратим внимание на то, что типы значений в списке значений должны соответствовать типам значений полей таблицы, например:
INSERT INTO friends VALUES ('Анна Осипова', '495-09-81')
В данном примере в таблицу friends добавляется новая запись с указанными значениями.
UPDATE - эта команда применяется для обновления данных в таблице и чаще всего используется совместно с оператором WHERE. Команда UPDATE имеет следующий синтаксис:
UPDATE имя_таблицы SET имя_поля = значение [WHERE критерий]
Если опустить оператор WHERE, то будут обновлены данные во всех определенных полях таблицы. Для примера, поменяем номер телефона Сергея Иванова:
UPDATE friends SET phone = '255-55-55' WHERE name = 'Сергей Иванов'
DELETE - как вы уже наверное поняли, эта команда служит для удаления записей из таблицы. Как и UPDATE, команда DELETE обычно используется с оператором WHERE, если этот оператор пропустить, то будут удалены все данные из указанной таблицы. Синтаксис команды DELETE выглядит следующим образом:
DELETE FROM имя_таблицы [WHERE критерий]
Для примера, давайте удалим Сергея Иванова из нашей таблицы :) :

DELETE FROM friends WHERE name = 'Сергей Иванов'

пятница, 11 января 2019 г.

Связи таблиц в MySQL

Связи таблиц в MySQL

Автор: Дмитрий Лебедев
Первоначально я предполагал сделать выпуск "Народной самодеятельности" со сборником ошибок, допущеных в основном мною :). Кое-что я собирал из статей о традиционных ошибках и т.п. Но жизнь оказывается куда интересней! Пишу про очередную самодеятельность прямо по горячим следам.
Итак, некоторый человек, назовем его Вася, спрашивает Общественность форума про MySQL.
В.: Не совсем понятно как создать связь между таблицами, чтобы в поле первой автоматически вставлялись данные второй (есно выборочно и есно по ИД).
Вариант типа SELECT db.user, db.delete_priv, user.user, user.delete_priv FROM db,user WHERE db.user = user.user не совсем подходит, так как связью это можно назвать с большой натяжкой...
O.: Честно говоря, не понимаю, почему эта связь тебе не подходит? Чем эта связь натянута?
В.: Ну не нравится мне связь, основанная на синтаксисе запросов... хотелось бы чего-нибудь более существенного, иначе я вообще не вижу смысла в ключах и индексах.
O.: Более существенное - ты имеешь в виду графический интерфейс как в MS Access?
К сожалению, сам Access работает точно так же — достаточно нажать кнопочку "SQL", и вы увидите эти связи "с большой натяжкой".
В.: Просто мне проще программно все это делать, благо таблицы небольшие... Раз в самом MySQL это не работает.
O.: Через вложенные циклы, рекурсии? Скажи зачем тогда тебе вообще база данных и таблицы?
В.: Никаких вложенных циклов и рекурсий — читаю н-ое колмчество массивов и с ними уже работаю.... А делать связь на основе селекта — не совсем то что мне нужно...
O.: Правильно... храни все в файликах, тогда вообще вопросов не будет.
В общем, считаю своим долгом разложить по полочкам связи таблиц.
Кстати, насчёт связей - вот мнение признанного авторитета - Voodoo ;) : более существенное - это поддержка reference www.mysql.com/documentation/mysql/commented/manual.php?section=CREATE_TABLE
create table.....
reference_definition:        REFERENCES tbl_name [(index_col_name,...)]
[MATCH FULL | MATCH PARTIAL]
[ON DELETE reference_option]
[ON UPDATE reference_option]
The FOREIGN KEY, CHECK, and REFERENCES clauses don't actually do anything. The syntax for them is provided only for compatibility, to make it easier to port code from other SQL servers and to run applications that create tables with references. See section 5.4 Functionality Missing from MySQL.
Связь без большой натяжки
Вообще, никакой натяжки в описании связей в запросе не было и нет - испокон веков БД работали именно так. Access со стрелочками и формопостроителями появился намного позднее.
Связь между таблицами - суть реляционной базы данных. В идеале вне базы данных не держится никакая информация. Внутри базы разные по сути вещи разделяются на разные таблицы - например, сообщения и форумы, сообщения и их авторы (если есть регистрация участников), возможно даже в отдельную таблицу выносится разрешения доступ к форумам персонально для каждого участника. При этом данные лежат там, где надо, не смешиваются друг с другом, и не повторяются лишний раз. Это и есть основной смысл реляционных БД. За исключением сложных задач (например, построить дерево обсуждений форума), выборка данных производится одним запросом. Никакие массивы использовать не надо.
Как делать неправильно
Например, вот так:
$res1 = mysql_query("SELECT id, name FROM rubs");
while ($row = mysql_fetch_row($res1))
  $rub[$row[0]] = $row[1];
Из запроса получены имена рубрик и записаны в массив $rub.
$res2 = mysql_query("SELECT id, url, name, rub FROM sites WHERE какое-то-там-условие");
while ($row = mysql_fetch_array($res2)) {
  echo "<a href=", $row["url"], ">", $row["name"], "</a>";
  echo "(рубрика <a href=rub.phtml?id=", $row["id"], ">";
  echo $rub[$row["rub"]], "</a><br>";
  };
Теперь выбираются новости, и вместо выбранного номера рубрики вставляем соответствующий элемент массива рубрик.
На самом деле, можно избавить себя от необходимости тащить сквозь всю программу этот массив $rub (а если у нас к рубрикам обращаются функции - что, через GLOBAL брать?), от возможности ошибиться с $rub[$row["rub"]] - если на странице несколько подобных запросов, то сделать опечатку где-нибудь легко.
Кроме того, под массив $rub требуется некоторый объем памяти (а если рубрик много?). В третьей версии PHP такой скрипт будет выполняться дольше, чем при использовании объединений таблиц, потому что он интерпретирует программу построчно при выполнении (в отличие от 4-го, который компилирует программу и только потом выполняет).
Как надо
В приведённом выше примере можно применить объединение таблиц и избавиться от описанных недостатков.
$res = mysql_query("SELECT sites.id, url, sites.name as sitename, rubs.name as rubsname, 
 rubs.id as rub_id FROM sites, rubs WHERE sites.rub=rubs.id 
 и-какое-то-там-условие");
while ($row = mysql_fetch_array($res2)) {
  echo "<a href=", $row["url"], ">", $row["sitename"], "</a>";
  echo "(рубрика <a href=rub.phtml?id=", $row["rub_id"], ">";
  echo $row["rubsname"], "</a><br>";
  };
Итак, здесь лучше использовать запрос "SELECT sites.id, url, sites.name as sitename, rubs.name as rubsname, rubs.id as rub_id FROM sites, rubs WHERE sites.rub=rubs.id". Получается, что мы имеем готовый массив, заботимся о выводе только его элементов и пишем меньше кода.
Синтаксис объединений таблиц
Простое соединение - INNER JOIN:
SELECT <fields> FROM table1 INNER JOIN table2 ON table1.field1=table2.field
или
SELECT <fields> FROM table1, table2 WHERE table1.field1=table2.field2
или
SELECT <fields> FROM table1 INNER JOIN table2 USING (field1)
если таблицы объединяются по полю field1.
В таком соединении выбираются только те строки таблиц, которые соответствуют условию объединения - равенство значений полей. Если для строки table1 нет соответствующей строки из table2, строка не попадает в итог запроса. Если же надо подсчитать количество сайтов в рубрике (продолжаю пример с каталогом), такой запрос не совсем подходит - в списке появятся только рубрики, в которых есть сайты. Для подобной операции нужно использовать LEFT JOIN.
SELECT <fields> FROM table1 LEFT JOIN table2 ON table1.field1=table2.field2
или
SELECT <fields> FROM table1 LEFT JOIN table2 USING (field1)
если таблицы объединяются по полю field1.
При этом соответствующей строки в table2 может и не быть, тогда в полях из table2 мы получим NULL, а если это групповая операция, как в случае с количеством сайтов в рубрике, тогда в поле будет 0:
SELECT rubs.id, name, COUNT(sites.id) AS sites FROM rubs LEFT JOIN sites ON rubs.id=sites.rub GROUP BY rubs.id
Заметьте: поля id есть в обеих таблицах, поэтому в их обозначении надо использовать имя таблицы. Кстати, если при объединении не используются групповые операции, всё равно лучше менять имя поля оператором AS, чтобы не возникало путаницы.

Постреляционная СУБД Postgres95

Постреляционная СУБД Postgres95


СУБД POSTGRES95 была спроектирована и разработана в Калифорнийском университете г. Беркли под руководством известного специалиста в области баз данных профессора Стоунбрейкера, который в 1975-1980 гг. создал довольно популярную реляционную СУБД Ingres. Направление POSTGRES принадлежит к числу так называемых постреляционных систем - к следующему этапу в развитии реляционных СУБД. В настоящее время основным предметом критики последних является не их недостаточная эффективность, а присущая этим системам некоторая ограниченность (прямое следствие простоты) при использовании в нетрадиционных областях, в которых требуются предельно сложные структуры данных. Другим, часто отмечаемым недостатком реляционных баз данных, является невозможность адекватного отражения семантики предметной области. Поэтому современные исследования в области постреляционных систем, главным образом, посвящены устранению именно этих недостатков, и во многом требования к этим системам означают просто необходимость реализации свойств, отсутствующих в большинстве текущих реляционных СУБД. В их число, например, входит полнота системы типов, поддержка иерархии и наследования типов, возможность управления сложными объектами и т.д. СУБД POSTGRES95, являясь постреляционной системой, сохраняет основные свойства реяционных СУБД и в то же время имеет свои, отличные от других, возможности.
СУБД POSTGRES95 поддерживает темпоральную модель хранения и доступа к данным. То есть для любого объекта данных, созданного в момент времени t1 и уничтоженного в момент времени t2, в БД сохраняются (и доступны пользователям) все его состояния во временном интервале (t1,t2). Обычные же БД хранят мгновенный снимок модели предметной области, и любое изменение в момент времени t некоторого объекта приводит к недоступности этого объекта в предыдущий момент времени. Хотя, на самом деле, в большинстве развитых СУБД предыдущее состояние объекта сохраняется в журнале изменений, но осуществления доступа со стороны пользователя нет.
В связи с этим, в POSTGRES95 пересмотрен механизм журнализации изменений, откатов транзакций и восстановления БД после сбоя. Особенность системы управления памятью заключается в том, что не ведется обычная журнализация и мгновенно обеспечивается корректное состояние БД с утратой состояния в оперативной памяти. Также система управления памятью поддерживает исторические данные, соответствующие возможности работы с которыми заложены в язык POSTQUEL. Запросы могут содержать выборку данных в определенное время, в определенном интервале времени. Например, результатом запроса:
SELECT city, population FROM cities['epoch','now'] WHERE city='Moscow';
будет являться следующая таблица: city population
Moscow7 360 000
Moscow8 950 000
Кроме этого, имеется возможность создавать версии отношений и допускается их последующая модификация с учетом изменений основных вариантов.
Основное решение этих аспектов состоит в том, что при модификации кортежа изменения производятся не на месте его хранения, а заводится новая запись, куда помещаются измененные поля. Эта запись, содержащая также и данные, характеризующие транзакцию, производившую изменения (в том числе и время ее завершения), подшивается в список к изменявшемуся кортежу.
Одним из основных положений реляционной модели данных является требование нормализации отношений: поля кортежей могут содержать лишь атомарные значения. Приведение исходного табличного представления предметной области к первой нормальной форме является основным шагом в процессе проектирования реляционной базы данных. В СУБД POSTGRES95 реализована "ненормализованная" реляционная модель данных, которая допускает хранение в качестве элемента кортежа многомерных массивов фиксированной и переменной длины, и других данных, в том числе абстрактных, определенных пользователями типов:
CREATE TABLE salary (name text,pay_by_quarter int4[ ], schedule char16[ ][ ]);
Это свойство POSTGRES95 сближает ее со свойствами объектно-ориентированных СУБД, хотя семантические возможности модели данных POPSTGRES95 существенно слабее.
Язык запросов СУБД POSTGRES95 - POSTQUEL- является вариантом нового стандарта языка SQL-3. Он имеет операторы для определения схемы базы данных (CREATE TABLE, ALTER TABLE), манипулирования данными (UPDATE- заменить, DELETE - удалить, SELECT- выбрать, INSERT- вставить и др.), операторы управления транзакциями, предоставлений и ограничений доступа и др. POSTQUEL, кроме этого, предоставляет много дополнительных возможностей. В их число входят расширенная система типов (кроме обычных типов int, float, real, smallint, char(N), varcha(N), date, time и др. реализована возможность создания пользователями произвольного числа своих типов), поддерживается иерархия и наследование классов, предоставляется возможность определения собственных функций, операторов и агрегатов. В таблицах могут храниться данные размером более 8 KB. Это позволяет осуществлять, так называемый, интерфейс больших объектов (Large Objects Interface), который применяет файл-ориентированный доступ к данным, объявленных как тип large. POSTQUEL не поддерживает подзапросы, но они могут легко быть осуществлены с помощью самостоятельно написанных SQL-функций.
POSTQUEL поддерживает два типа функций: SQL-функции и функции, написанные на языке программирования, например, на Си. Кроме функций, пользователь может создавать свои агрегаты и операторы. POSTGRES95 позволяет легко вводить новые структуры, используя не физическую, а логическую модель хранения данных. В системных каталогах POSTGRES95, в отличие от стандартных реляционных систем, хранится информация не только об отношениях и атрибутах, но также и информация о типах, функциях, методах доступа и т.п. В POSTGRES95 системные каталоги представлены следующими классами: pg_database - базы данных; pg_class - отношения; pg_attribut - атрибуты; pg_proc - процедуры (и на Си, и на SQL); pg_type - типы; pg_aggregate - функции и агрегаты; и др. Каждый класс располагается в файле с соответствующим именем, которое начинается с pg_, куда помещаются все вносимые пользователем изменения при создании таблиц, типов, функций и т.д. Между классами установлены отношения, которые позволяют связывать различные структуры и осуществлять внутренние операции между ними.

Функция SQL COUNT

Функция SQL COUNT

Функция SQL COUNT возвращает количество записей в запросе.
Синтаксис функции COUNT:
SELECT COUNT(expression) FROM table WHERE predicates;
Функция COUNT принимает один из нескольких параметров:
  • * — означает то, что функция COUNT возвращает все записи в таблице;
  • column_name — функция COUNT возвращает количество записей конкретного столбца (только NOT NULL);
  • DISTINCT column_name — функция COUNT возвращает количество только разных записей конкретного столбца (только NOT NULL).

Примеры SQL COUNT

Напишем запрос SELECT COUNT, возвращающий количество записей в таблице users.
SELECT COUNT(*) FROM users;
Напишем запрос, возвращающий количество записей столбца User_ID в таблице users, дата которых равна «20.05.2012».
SELECT COUNT(User_ID) FROM users WHERE date = «20.05.2012»;
Теперь получим количество только разных записей столбца User_ID.
SELECT COUNT(DISTINCT User_ID) FROM users;

Функция SQL AVG

Функция SQL AVG

Функция SQL AVG необходимо в случае, если требуется вычислить среднее значение числового столбца в таблице.
Среднее значение функция AVG() в SQL вычисляет среднее значение столбца путем суммирования всех значений записей столбца и деления на количество записей.
Рассмотрим пример. Допустим в таблице Price есть столбец Price_unit. В этой таблице содержатся 5 записей. Значения полей столбца Price_unit 3, 5, 14, 38 и 83. Выполним запрос, возвращающий среднее значение столбца Price_unit
SELECT AVG(Price_unit) AS PriceAvg FROM Price;
Результатом выполнения запроса будет
PriceAvg = 28,6

SQL And & Or

AND & OR

Операторы AND & OR используются для составления условий в запросах.
Описание операторов AND & OR в SQL
AND — оператор, который отображает только те записи, когда первое и второе условие является правдой / true.
OR — оператор, который отображает только те записи, когда хотя бы одно из двух условий является правдой / true.
Поскольку допускается наличие в базе данных неопределенных значений, то вычисление условия поиска производится не в булевой, а в трехзначной логике со значениями truefalse и unknown (неизвестно). Для любого предиката известно, в каких ситуациях он может порождать значение unknown. Булевские операции ANDOR и NOT работают в трехзначной логике следующим образом:
ЗначениеОперацияЗначениеРезультат
trueANDunknownunknown
unknownANDtrueunknown
unknownANDunknownunknown
trueORunknowntrue
unknownORtruetrue
unknownORunknownunknown
NOTunknownunknown

Пример оператора AND

Пример 1. Выборка (SELECT) записи, где поле fname равно ‘Ivan’ AND(и) поле lname равно ‘Abrakov’.
SELECT * FROM table_name WHERE fname=’Ivan’ AND lname=’Abrakov’;

Пример оператора OR

Пример 2. Выборка (SELECT) записи, где поле fname равно ‘Ivan’ OR(или) поле fname равно ‘Alex’.
SELECT * FROM table_name WHERE fname=’Ivan’ OR fname=’Alex’;

Пример комбинирования AND & OR

Пример 3. Выборка (SELECT) записи, которые соответствуют условию: поле lname должно быть равно ‘Abrakov’ AND(и) поле fname должно принимать значение ‘Ivan’ OR(или) значение ‘Alex’.
SELECT * FROM table_name WHERE lname=’Abrakov’ AND (fname=’Ivan’ ORfname=’Alex’);

Ограничения FOREIGN KEY

Ограничения FOREIGN KEY

FOREIGN KEY используется для ограничения по ссылкам.
Когда все  значения в одном поле таблицы представлены в поле другой таблицы, говорится, что первое поле ссылается на второе. Это указывает на прямую связь между значениями двух полей.
Когда одно  пол в таблице ссылается на другое, оно называется внешним ключом;  а поле на которое оно ссылается,  называется родительским  ключом. Имена внешнего ключа и родительского ключа не обязательно должны быть одинаковыми. Внешний ключ может иметь любое число полей,  которые все обрабатываются как  единый  модуль.  Внешний ключ и родительский ключ, на который он ссылается,  должны иметь одинаковый номер и тип поля,  и находиться в одинаковом порядке. Когда поле является внешним ключом, оно определеным образом связано с таблицей, на которую он ссылается. Каждое значение, (каждая строка ) внешнего ключа должно недвусмысленно ссылаться к одному и только этому значению (строке) родительского ключа. Если это условие соблюдается, то база данных находится в состоянии ссылочной целостности.
SQL поддерживает  ссылочную целостность с ограничением FOREIGN KEY. Эта функция должна ограничивать значения, которые  можно  ввести в базу данных, чтобы заставить внешний ключ и родительский ключ соответствовать принципу  ссылочной целостности. Одно из действий ограничения FOREIGN KEY — это отбрасывание значений для полей, ограниченных как внешний ключ, который еще не представлен в родительском ключе. Это ограничение также воздействует на способность изменять или удалять значения родительского ключа
Ограничение FOREIGN KEY используется в команде CREATE TABLE (или ALTER TABLE (предназначена для модификации стуктуры таблицы), содержащей поле, которое объявлено внешним ключом. Родительскому ключу дается имя, на которое  имеется ссылка внутри ограничения FOREIGN KEY.
Подобно большинству ограничений, оно может быть ограничением таблицы или столбца, в форме таблицы позволяющей использовать многочисленные поля как один внешний ключ.
Синтаксис ограничения таблицы FOREIGN KEY:
FOREIGN KEY <column list> REFERENCES
<pktable> [ <column list> ]
Первый список столбцов — это список из одного или более столбцов таблицы,  которые отделены запятыми и будут созданы или изменены  этой командой.
Pktable — это таблица содержащая родительский ключ. Она может быть таблицей,  которая создается или изменяется текущей командой.
Второй  список столбцов — это список столбцов, которые будут составлять родительский ключ. Списки двух столбцов должны быть совместимы, т.е.:
  • иметь одинаковое число столбцов
  • в данной последовательности, первый, второй, третий, и т.д., столбцы списка столбцов внешнего ключа, должны иметь одинаковые типы данных и размеры, что и первый, второй, третий, и т.д., столбцы списка столбцов родительского ключа.
  • столбцы в списках обоих столбцов не должны иметь одинаковых имен.

FOREIGN KEY Пример 1

CREATE TABLE Student
( Kod_stud integer NOT NULL PRIMARY KEY,
Kod_spec integer NOT NULL,
Fam char(30) NOT NULL UNIQUE,
Adres char(50),
Ball decimal),
FOREIGN KEY (Kod_spec) REFERENCES Spec (Kod_spec)
);
При использовании ALTER TABLE вместо CREATE TABLE, для применения ограничения FOREIGN KEY, значения, указываемые во внешнем ключе и родительском ключе, должны быть в состоянии ссылочной целостности. Иначе команда будет отклонена.
Используя ограничение FOREIGN KEY таблицы или столбца, можно не указывать список столбцов родительского ключа, если родительский ключ имеет ограничение PRIMARY KEY. Естественно, в случае ключей со многими полями, порядок столбцов во внешних и первичных ключах должен совпадать, и, в любом случае, принцип совместимости между двумя ключами все еще применим.

FOREIGN KEY Пример 2

CREATE TABLE Student (
Kod_stud integer NOT NULL PRIMARY KEY,
Fam char(30) NOT NULL UNIQUE,
Adres char(50),
Ball decimal),
Kod_spec integer REFERENCES Spec
);
Поддержание ссылочной целостности требует некоторых ограничений на значения, которые  могут быть представлены в полях,  объявленных как внешний ключ и родительский ключ. Родительский ключ должен быть структурен, чтобы гарантировать, что каждое значение внешнего ключа будет соответствовать одной указанной строке. Это означает, что он (ключ) должен быть уникальным и не содержать никаких пустых значений(NULL).
Этого не достаточно для родительского ключа в случае выполнения такого требования,  как при объявлении внешнего ключа. SQL должен быть уверен, что двойные значения или пустые значения (NULL) не были введены в  родительский ключ.  Следовательно необходимо убедиться, что все поля, которые используются как родительские ключи,  имеют или ограничение PRIMARY KEY или ограничение UNIQUE, наподобие ограничения NOT NULL.
Ссылка внешних  ключей только на первичные ключи — хорошая стратегия. Когда используются внешние  ключи,  они связываются не просто с родительскими ключами, на которые они ссылаются; они связываются с определенной строкой таблицы, где этот родительский ключ будет найден. Сам по себе родительский ключ не обеспечивает никакой информации, которая бы не была уже представлена во внешнем ключе.
Так как цель первичного ключа состоит в том,  чтобы идентифицировать уникальность строки, это более логичный и менее  неоднозначный  выбор для внешнего ключа.  Для любого внешнего ключа, который использует уникальный ключ как родительский ключ, необходимо создать внешний ключ, который бы использовал первичный ключ той же самой таблицы для того же самого действия. Внешний ключ, который не имеет никакой другой цели кроме связывания строк, напоминает первичный ключ, используемый исключительно для идентификации строк, и является хорошим средством сохранения структуры  базы данных ясной и простой. Внешний ключ может содержать только те значения, которые фактически представлены в родительском ключе или пустые (NULL). Попытка ввести другие значения в этот ключ будет отклонена.

FOREIGN KEY Пример 3

CREATE TABLE payment (
sh_payout integer,
sh_eml integer,
date_payout date,
summ_payout real,
FOREIGN KEY (sh_eml) REFERENCES k_sotr2 (eid)
);
В данном примере FOREIGN KEY столбец sh_eml связывается со столбцом eid из таблицы k_sotr2.
Основные ключевые слова, используемые в статье:

foreign key, foreign key mysql, foreign key oracle, foreign key sql, foreign key references

Первичный ключ PRIMARY KEY

Первичный ключ PRIMARY KEY

PRIMARY KEY — первичный ключ, ограничение, позволяющее однозначно идентифицировать каждую запись в таблице SQL.
PRIMARY KEY Oracle
Первичный Ключ (PRIMARY KEY) может ограничивать таблицы или их столбцы. Это ограничение работает так же как и ограничение UNIQUE. Но следует учитывать различие между первичными ключами и уникальностью столбцов в способе их использования с внешними ключами. Первичные ключи не могут позволять значений NULL. Это означает что, подобно полям в ограничении UNIQUE, любое поле, используемое в ограничении PRIMARY KEY, должно уже быть обьявлено NOT NULL.
PRIMARY KEY Oracle. Пример №1.
Пример создания таблицы SQL с ограничением PRIMARY KEY:
CREATE TABLE Student
( Kod_stud integer NOT NULL PRIMARY KEY,
Fam char(30) NOT NULL UNIQUE,
Adres char(50),
Ball decimal);
Лучше всего помещать ограничение PRIMARY KEY в поле (или в поля), которое будет образовывать уникальный идентификатор строки,  и сохранить ограничение UNIQUE для полей  которые  должны быть  уникальными логически (такие как номера телефона или поле sname), а не для идентификации строк. Ограничение PRIMARY KEY может также быть применено для  многочисленных  полей,  составляющих уникальную комбинацию значений:
PRIMARY KEY Oracle. Пример №2.
CREATE TABLE  Student
( Fam char (30) NOT NULL,
Im char (30) NOT NULL
Adres char (50),
PRIMARY KEY (Fam, Im));

PRIMARY KEY MySQL


PRIMARY KEY SQL / MySQL. Пример №3.
CREATE TABLE Persons (
P_Id int NOT NULL,
LastName varchar(255) NOT NULL,
FirstName varchar(255),
Address varchar(255),
City varchar(255),
PRIMARY KEY (P_Id));

PRIMARY KEY SQL / MySQL. Пример №4.
CREATE TABLE `ad_packages` (
`id` int(111) NOT NULL auto_increment,
`title` varchar(132) NOT NULL default »,
`price` float NOT NULL default ‘0’,
`type` varchar(22) NOT NULL default »,
`c_type` enum(‘cash’,’points’,’rur’) NOT NULL default ‘cash’,
PRIMARY KEY (`id`)
);

PRIMARY KEY SQL / MySQL. Пример №5.
CREATE TABLE `gamestat` (
`id` int(11) NOT NULL auto_increment,
`game` varchar(10) NOT NULL default ‘tuz’,
`stavok` int(11) NOT NULL default ‘0’,
`usd` float NOT NULL default ‘0’,
`rur` float NOT NULL default ‘0’,
`point` float NOT NULL default ‘0’,
`bank_usd` decimal(12,2) NOT NULL default ‘0.00’,
`bank_rur` decimal(12,2) NOT NULL default ‘0.00’,
`bank_point` decimal(12,2) NOT NULL default ‘0.00’,
PRIMARY KEY (`id`)
);