Руководство по переходу с MySQL на SQL Server 2005 Технический документ по SQL Server Авторы: Александр Павлов, Юрий Русаков Технические рецензенты: Ирина Балина, Дмитрий Балин Дата публикации: март 2008 г. Применяется к: SQL Server 2005 с пакетом обновления 2 Сводка: в данном руководстве по переходу описываются различия между СУБД MySQL и SQL Server 2005, а также действия, необходимые для преобразования баз данных MySQL в базы данных SQL Server. Авторские права Содержащиеся в документе сведения отражают текущую позицию корпорации Майкрософт в отношении обсуждаемых вопросов на момент публикации. Поскольку корпорация Майкрософт должна реагировать на изменение рыночных условий, данный документ не следует рассматривать как обязательство со стороны корпорации Майкрософт и корпорация Майкрософт не гарантирует точности представленных сведений после его публикации. Данный документ носит исключительно информационный характер. КОРПОРАЦИЯ МАЙКРОСОФТ НЕ ПРЕДОСТАВЛЯЕТ НИКАКИХ ГАРАНТИЙ, ЯВНЫХ, ПОДРАЗУМЕВАЕМЫХ ИЛИ ПРЕДУСМОТРЕННЫХ ЗАКОНОМ, ОТНОСИТЕЛЬНО СВЕДЕНИЙ, СОДЕРЖАЩИХСЯ В ДАННОМ ДОКУМЕНТЕ. Ответственность за соблюдение всех применимых законов об авторском праве лежит на пользователе. Согласно законам об авторских правах никакие части этого документа нельзя воспроизводить, хранить или использовать в поисковых системах или передавать в любой форме (электронной, механической, в виде фотокопий, записей или иными способами) и в любых целях без письменного разрешения корпорации Майкрософт. Корпорация Майкрософт может являться правообладателем патентов и поданных заявок на получение патента, товарных знаков, авторских прав и прочих объектов интеллектуальной собственности, которые имеют отношение к содержанию данного документа. Предоставление документа не означает передачи какой-либо лицензии на использование таких патентов, товарных знаков, авторских прав и других объектов интеллектуальной собственности, за исключением случаев, явно оговоренных в лицензионном соглашении корпорации Майкрософт. © Корпорация Майкрософт (Microsoft Corporation), 2008. Все права защищены. Microsoft, SQL Server и Visual C++ являются охраняемыми товарными знаками корпорации Майкрософт в США и других странах. Использованные в документе названия реальных компаний или продуктов могут быть товарными знаками соответствующих владельцев. Содержание Введение ............................................................................................................................................. 3 Переход с MySQL на SQL Server 2005............................................................................................ 3 Основные действия переноса ..................................................................................................... 3 Преобразование объектов базы данных .................................................................................... 4 Перенос типов данных MySQL ....................................................................................................... 4 Сопоставление типов ................................................................................................................... 5 Проблемы переноса типов данных ............................................................................................. 8 Числовые типы данных .......................................................................................................... 8 Типы данных даты и времени ............................................................................................. 11 Строковые типы .................................................................................................................... 14 Типы данных ENUM и SET .................................................................................................. 16 Другие типы данных ............................................................................................................. 20 Неявное преобразование типов данных ............................................................................ 20 Значения типов данных по умолчанию .............................................................................. 21 Проблемы перехода с MySQL ....................................................................................................... 22 Операторы ................................................................................................................................... 22 Операторы сравнения ......................................................................................................... 22 Битовые операторы ............................................................................................................. 24 Операторы присвоения........................................................................................................ 25 Переменные ................................................................................................................................ 26 Служебные инструкции .............................................................................................................. 28 Инструкции определения данных ............................................................................................. 29 Предложения IF NOT EXISTS, IF EXISTS и REPLACE ..................................................... 29 Временные таблицы ............................................................................................................ 35 Ключевое слово SCHEMA в инструкциях DATABASE ...................................................... 39 Предложения CHARACTER SET и COLLATE в инструкциях DDL ................................... 39 Инструкция CREATE INDEX ................................................................................................ 40 Инструкция CREATE TABLE ................................................................................................ 43 Инструкция ALTER TABLE ................................................................................................... 48 Инструкция RENAME DATABASE ....................................................................................... 53 Инструкция RENAME TABLE ............................................................................................... 54 Инструкции CREATE VIEW, ALTER VIEW, DROP VIEW ................................................... 55 Инструкции CREATE EVENT, ALTER EVENT, DROP EVENT ........................................... 57 Инструкции CREATE, ALTER, DROP PROCEDURE/FUNCTION ...................................... 57 Инструкции обработки данных .................................................................................................. 62 Предложение LIMIT .............................................................................................................. 62 Инструкция DELETE ............................................................................................................. 63 Инструкция UPDATE ............................................................................................................ 65 © Корпорация Майкрософт (Microsoft Corporation), 2008. Руководство по переходу с MySQL на SQL Server 2005 2 Инструкция INSERT.............................................................................................................. 66 Инструкция REPLACE .......................................................................................................... 69 Инструкция SELECT ............................................................................................................. 70 Инструкции SELECT…INTO и LOAD DATA INFILE............................................................ 71 Предложения GROUP BY, HAVING и ORDER BY ............................................................. 72 Соединения JOIN ................................................................................................................. 75 Subqueries ............................................................................................................................. 78 Подготовленные инструкции ............................................................................................... 79 Команда DO .......................................................................................................................... 79 Обработчики ......................................................................................................................... 80 Модификаторы ..................................................................................................................... 81 Инструкции транзакций и блокировки ....................................................................................... 81 Инструкции BEGIN TRANSACTION..................................................................................... 81 Инструкции END TRANSACTION ........................................................................................ 83 Именованные инструкции SAVEPOINT транзакций .......................................................... 83 Инструкции SET AUTOCOMMIT .......................................................................................... 85 Инструкции LOCK TABLES и UNLOCK TABLES ................................................................ 86 Инструкция SET TRANSACTION ISOLATION LEVEL ........................................................ 86 Инструкции транзакций XA .................................................................................................. 86 Инструкции администрирования баз данных ........................................................................... 87 Инструкции управления учетными записями .................................................................... 87 Инструкции обслуживания таблиц ...................................................................................... 87 Инструкция SET .................................................................................................................... 87 Инструкция SHOW ................................................................................................................ 89 Другие административные инструкции .............................................................................. 90 Хранимые процедуры и функции (процедуры) ........................................................................ 90 Инструкции CALL .................................................................................................................. 90 Составные инструкции ......................................................................................................... 91 Локальные переменные ....................................................................................................... 92 Условия и обработчики ........................................................................................................ 95 Курсоры ................................................................................................................................. 96 Конструкции управления потоком ....................................................................................... 98 Процедуры .......................................................................................................................... 101 Триггеры .................................................................................................................................... 102 Режим SQL (системная переменная SQL_MODE) ................................................................ 105 Перенос системных функций MySQL ........................................................................................ 106 Эквивалентные функции .......................................................................................................... 106 Неподдерживаемые функции .................................................................................................. 106 Эмулируемые функции ............................................................................................................ 106 Заключение .................................................................................................................................... 113 © Корпорация Майкрософт (Microsoft Corporation), 2008. Руководство по переходу с MySQL на SQL Server 2005 3 Введение В этом руководстве по переходу дается обзор процедур, проблем и решений для перехода с MySQL 5 на Microsoft® SQL Server™ 2005. Здесь представлены три основных раздела: Перенос типов данных MySQL. Описывается сопоставление типов данных, и добавляются примечания о соответствующих проблемах преобразования. Проблемы перехода с MySQL. Дается обзор трудностей, с которыми можно столкнуться при переходе с MySQL на SQL Server 2005, и предлагаются возможные решения. Перенос системных функций MySQL. Рассматриваются обращения к системным функциям MySQL, подразделяемым на эквивалентные функции, неподдерживаемые функции и эмулируемые функции. Переход с MySQL на SQL Server 2005 Ниже описываются основные, обобщенные действия для переноса баз данных MySQL на SQL Server 2005, а также то, что необходимо знать о преобразовании объектов базы данных. Основные действия переноса Перенос баз данных MySQL 1. Решите, как будете сопоставлять базы данных MySQL с SQL Server 2005. Имеются две основные возможности: Сопоставить каждую базу данных MySQL с отдельной базой данных SQL Server. Например, можно сопоставить базу данных MySQL MyDB с базой данных SQL Server MyDB. Сопоставить каждую базу данных MySQL с единой базой данных SQL Server, но с отдельной схемой. Например, можно сопоставить базу данных MySQL MyDB с базой данных SQL Server MySQLDatabases и схемой MyDB. В SQL Server схемы не обязательно привязаны к определенному пользователю или входу в систему и на одном сервере содержится несколько баз данных. 2. Преобразуйте объекты баз данных: это таблицы, ограничения таблиц, индексы, представления, процедуры, функции и триггеры. 3. Сопоставьте типы данных из MySQL с типами данных в SQL Server. 4. Перепишите представления, процедуры и функции в соответствии с синтаксисом SQL Server. 5. Если необходимо, измените приложения таким образом, чтобы они могли подключаться к SQL Server 2005 и работать с данными. После успешного преобразования базы данных перенесите данные из прежней базы данных MySQL во вновь созданную базу данных SQL Server 2005. Для этого можно использовать, например, службы интеграции SQL Server (SQL Server Integration Service, SSIS). © Корпорация Майкрософт (Microsoft Corporation), 2008. Руководство по переходу с MySQL на SQL Server 2005 4 Преобразование объектов базы данных В этом разделе приводятся некоторые соображения, которые необходимо принять во внимание при преобразовании объектов базы данных. Имена объектов схемы В SQL Server 2005 имя объекта может содержать до 128 символов. Имена идентификаторов, не заключаемых в кавычки, должны подчиняться следующим правилам: Первым символом должны быть буква, цифра, подчеркивание (_), знак @ или знак номера (#). В качестве последующих символов можно использовать буквы, цифры, подчеркивание, знак @, знак номера, знак доллара. Нельзя указывать в качестве идентификатора зарезервированное слово Transact-SQL. Внутри имени нельзя использовать пробелы или специальные символы. Идентификаторы, начинающиеся со знака @ или знака номера, имеют особые значения. Со знака @ начинаются имена локальных переменных. Со знака номера начинаются имена временных таблиц. Для заключения в кавычки имени идентификатора в Transact-SQL необходимо использовать квадратные скобки ([]). Таблицы, ограничения, индексы и представления Для преобразования таблиц используйте сопоставление типов данных столбцов (см. раздел Сопоставление типов далее в этом руководстве). В SQL Server 2005 поддерживаются следующие ограничения таблиц (столбцов): NOT NULL, UNIQUE, PRIMARY KEY, FOREIGN KEY, CHECK. Преобразуйте каждый тип ограничения в соответствии с синтаксисом Transact-SQL. Инструкции SELECT с предложением VIEW также следует преобразовать соответственно синтаксису Transact-SQL. Хранимые процедуры и пользовательские функции Преобразуйте хранимые процедуры и функции с использованием синтаксиса Transact-SQL. SQL Server 2005 не поддерживает инструкции DML в пользовательских функциях. Это означает, что вы не можете изменить какие-либо данные изнутри функции. Триггеры В SQL Server 2005 отсутствуют триггеры BEFORE. Преобразуйте несколько триггеров BEFORE в единый триггер INSTEAD OF. Перенос типов данных MySQL В этом разделе описываются сопоставления типов данных MySQL и SQL Server 2005 и различия между ними, обработка определенных типов данных, а также предлагаются решения проблем, связанных с типами данных. © Корпорация Майкрософт (Microsoft Corporation), 2008. Руководство по переходу с MySQL на SQL Server 2005 5 Сопоставление типов Ниже приведено рекомендуемое сопоставление типов для преобразования столбцом таблиц, аргументов подчиненных процедур, возвращенных значений и локальных переменных типов данных. Тип в MySQL Сопоставление в SQL Server 2005 Примечания BIT (N) varbinary (8) Двоичное значение длиной N бит. N = 1..64 TINYINT (M) tinyint M — количество десятичных знаков в выходных данных для этого значения tinyint, smallint, int, bigint, numeric(p,s), decimal(p,s), float(p), double precision, real, smallmoney, money SMALLINT (M) smallint M — количество десятичных знаков в выходных данных для этого значения tinyint, smallint, int, bigint, numeric(p,s), decimal(p,s), float(p), double precision, real, smallmoney, money MEDIUMINT (M) int M — количество десятичных знаков в выходных данных для этого значения tinyint, smallint, int, bigint, numeric(p,s), decimal(p,s), float(p), double precision, real, smallmoney, money INT (M) int M — количество десятичных знаков в выходных данных для этого значения tinyint, smallint, int, bigint, numeric(p,s), decimal(p,s), float(p), double precision, real, smallmoney, money BIGINT (M) bigint M — количество десятичных знаков в выходных данных для этого значения tinyint, smallint, int, bigint, numeric(p,s), decimal(p,s), float(p), double precision, real, smallmoney, money FLOAT (P) float (P) FLOAT [(P, S)] float (24) DOUBLE [(P, S)] float (53) BOOL, BOOLEAN = TINYINT (1) INTEGER (M) DOUBLE PRECISION [(P, S)] REAL [(P, S)] Возможное сопоставление numeric(p,s), decimal(p,s), float(p), double precision, real, smallmoney, money В MySQL допускается нестандартный синтаксис: FLOAT(P,S), или REAL(P,S), или DOUBLE PRECISION(P,S). Здесь «(P,S)» означает, что отображается до P знаков значения, из которых до S знаков может быть после запятой. MySQL выполняет округление при сохранении значений. numeric(p,s), decimal(p,s), float(p), double precision, real, smallmoney, money numeric(p,s), decimal(p,s), float(p), double precision, real, smallmoney, money © Корпорация Майкрософт (Microsoft Corporation), 2008. Руководство по переходу с MySQL на SQL Server 2005 6 Если M и D опущены, предельный размер хранящихся значений определяется возможностями оборудования DECIMAL [(P [, S])] decimal [(P [, S])] numeric [(P [, S])] DEC [(P [, S])] NUMERIC [(P [, S])] FIXED [(P [, S])] Десятичные типы могут содержать до 65 цифр. Для десятичных значений с точностью свыше 38 необходимо использовать тип данных float или double numeric(p,s), decimal(p,s), float(p), double precision, real, smallmoney, money В MySQL можно сохранять даты с 0000-00-по 9999-12-31. В MySQL можно сохранять нулевое значение года, месяца и дня smalldatetime, datetime DATETIME datetime DATE datetime TIME datetime Диапазон от -838:59:59 до 838:59:59 smalldatetime, datetime, varchar, nvarchar TIMESTAMP smalldatetime Диапазон — с 1970-01-01 00:00:00 до 2037 года включительно. Если не определено при преобразовании, данный тип получает текущее значение datetime datetime, rowversion, timestamp, varbinary(8), binary(8) YEAR [(2| 4)] smallint В четырехзначном формате доступные значения — с 1901 по 2155, а также 0000. В двухзначном формате доступные значения — с 70 по 69 (что соответствует годам с 1970 по 2069) datetime, varchar(4) [NATIONAL] CHAR (N) nchar (N) Диапазон N — от 0 до 255 знаков char, varchar, nchar, nvarchar Диапазон N — от 0 до 65 535. char, varchar, nchar, nvarchar nchar smalldatetime, datetime [NATIONAL] CHAR [NATIONAL] VARCHAR (N) CHARACTER VARYING (N) nvarchar (N | max) Если N<=8000, то используется nvarchar(N), в противном случае — nvarchar(max) © Корпорация Майкрософт (Microsoft Corporation), 2008. Руководство по переходу с MySQL на SQL Server 2005 TINYTEXT nvarchar (255) TEXT (N) nvarchar (N | max) 7 char, varchar, nchar, nvarchar Текстовый столбец длиной до 65 535 знаков. Если N<=8000, то используется nvarchar(N), в противном случае — nvarchar(max) char, varchar, nchar, nvarchar, varchar(max), nvarchar(max) MEDIUMTEXT nvarchar (max) char, varchar, nchar, nvarchar, varchar(max), nvarchar(max) LONGTEXT nvarchar (max) char, varchar, nchar, nvarchar, varchar(max), nvarchar(max) BINARY (N) binary (N) binary, varbinary, char, varchar, nchar, nvarchar VARBINARY (N) varbinary (N) binary, varbinary, char, varchar, nchar, nvarchar TINYBLOB varbinary (255) binary, varbinary, varbinary(max) BLOB (N) varbinary (N | max) Столбец BLOB длиной до 65 535 знаков. binary, varbinary, varbinary(max) Если N<=8000, то используется nvarchar(N), в противном случае — nvarchar(max) MEDIUMBLOB varbinary (max) binary, varbinary, varbinary(max) LONGBLOB varbinary (max) binary, varbinary, varbinary(max) ENUM См. типы данных ENUM и SET в данном руководстве. SET См. типы данных ENUM и SET в данном руководстве. Примечание. Числовые типы MySQL имеют флаг UNSIGNED. Их следует преобразовывать в более крупный числовой тип. © Корпорация Майкрософт (Microsoft Corporation), 2008. Руководство по переходу с MySQL на SQL Server 2005 8 Проблемы переноса типов данных В этом разделе описываются проблемы, возникающие при преобразовании типов данных. Причина каждой проблемы заключается в функции MySQL, которая не поддерживается в SQL Server. Числовые типы данных Проблема: беззнаковые типы данных Все целые типы данных в MySQL (TINYINT, SMALLINT, MEDIUMINT, INT, BIGINT) могут иметь дополнительный атрибут UNSIGNED. Беззнаковые значения можно использовать, чтобы разрешить в столбце только неотрицательные значения, когда требуется большой верхний предел диапазона допустимых значений для столбца. Используя беззнаковые значения, можно также разрешить только неотрицательные значения в столбцах, содержащих типы данных с плавающей (FLOAT, DOUBLE) и фиксированной запятой (DECIMAL). Пример create table numeric_unsigned (t tinyint unsigned, s smallint unsigned, m mediumint unsigned, i int unsigned, b bigint unsigned); insert numeric_unsigned values (255, 65535, 16777215, 4294967295, 18446744073709551615); create table point_unsigned (f float unsigned, d double unsigned); insert point_unsigned values (-1.1234567890,-1.12345678901234567890); insert point_unsigned values ( 5.1234567890, 5.12345678901234567890); select * from point_unsigned; -- 0 0 -- 5.12346 5.12345678901235 Решение Для исключения отрицательных значений используйте ограничения CHECK. Это самый простой способ, но у него имеется один недостаток — при попытке назначить недопустимое значение вы получите исключение. Можно также исключать отрицательные значения с помощью триггера INSERT или UPDATE. Этот метод позволяет корректировать недопустимые значения перед сохранением их в базу данных. Проблема: операции с беззнаковыми значениями При вычитании целочисленных значений, одно из которых имеет тип UNSIGNED, результат будет беззнаковым. © Корпорация Майкрософт (Microsoft Corporation), 2008. Руководство по переходу с MySQL на SQL Server 2005 9 Пример create table unsing (a int unsigned, b int); insert unsing values (1,2),(4,3),(10,100); select a-b from unsing; -- 18446744073709551615 -- 1 -- 18446744073709551526 Решение Используйте функцию CASE, чтобы вычислить результат операции с беззнаковыми значениями. Проблема: ширина отображения целочисленных значений и атрибут ZEROFILL В MySQL можно указывать ширину отображения целого (TINYINT, SMALLINT, MEDIUMINT, INT, BIGINT) значения в скобках следом за базовым ключевым словом типа (например, INT(4)). Это необязательное указание ширины отображения применяется для дополнения пробелами слева отображения тех чисел, ширина которых меньше ширины, указанной для столбца. Ширина отображения не ограничивает ни диапазон значений, которые можно сохранять в столбце, ни число цифр, отображаемых для значений, ширина которых превышает ширину, указанную для столбца. См. также: атрибут ZEROFILL Решение Пропускайте эти атрибуты во время преобразования. Форматируйте выводимые данные с помощью таких функций, как STR и CONVERT. Проблема: атрибут ZEROFILL При использовании совместно с дополнительным атрибутом ZEROFILL вместо применяемого по умолчанию дополнения пробелами в MySQL используется дополнение нулями. Если атрибут ZEROFILL указывается для числового столбца, MySQL автоматически добавляет столбцу атрибут UNSIGNED. Пример create table table_zerofill (a int(2) zerofill, b int(4) zerofill, c int(8) zerofill, d decimal(5,2) zerofill); insert table_zerofill values (2,4,8,1.23); select concat('BEGIN',a,b,c,d,'END') from table_zerofill -- BEGIN02000400000008001.23END © Корпорация Майкрософт (Microsoft Corporation), 2008. Руководство по переходу с MySQL на SQL Server 2005 10 create table point_zerofill (f float zerofill, d double zerofill); insert point_zerofill values (-1.1234567890,-1.12345678901234567890); insert point_zerofill values ( 5.1234567890, 5.12345678901234567890); select * from point_zerofill; -- 000000000000 0000000000000000000000 -- 000005.12346 0000005.12345678901235 Решение Пропускайте эти атрибуты во время преобразования. Форматируйте выводимые данные с помощью таких функций, как STR, REPLICATE и REPLACE. Проблема: точность и масштаб типов данных FLOAT и DOUBLE В MySQL типы данных FLOAT и DOUBLE могут иметь точность и масштаб. Пример create table table_float (f2 float|double(10,2), f5 float|double(10,5), f7 float|double(10,7)); insert into table_float values (1.1234567,1.1234567,1.1234567); insert into table_float values (12345.1234567,12345.1234567,12345.1234567); select * from table_float; -- 1.12 1.12346 1.1234567 -- 12345.12 12345.12305 1000.0000000 Решение Значения типа FLOAT и DOUBLE с точностью и масштабом можно округлять в триггерах или в инструкциях DML при помощи функции ROUND. Проблема: максимальное количество цифр для типа данных DECIMAL В MySQL максимальное количество цифр для типа данных DECIMAL равно 65. Пример create table table_decimal (d decimal(65), m decimal(65,30)); insert table_decimal values (1234567890123456789012345678901234567890123456789012345678901234567890, 1234567890123456789012345678901234567890123456789012345678901234567890); select * from table_decimal; -- 99999999999999999999999999999999999999999999999999999999999999999 -- 99999999999999999999999999999999999.999999999999999999999999999999 © Корпорация Майкрософт (Microsoft Corporation), 2008. Руководство по переходу с MySQL на SQL Server 2005 11 Решение Для десятичных значений с точностью свыше 38 необходимо использовать тип данных float или double. Типы данных даты и времени Проблема: «нулевые» значения В MySQL можно сохранять значение «0000-00-00» в качестве «фиктивной даты», если не используется SQL-режим NO_ZERO_DATE. Недопустимые значения DATETIME, DATE, YEAR или TIMESTAMP преобразуются в «нулевое» значение соответствующего типа ('0000-00-00 00:00:00', '0000-00-00' или '0000'). Для типов времени и даты, отличных от TIMESTAMP в MySQL, по умолчанию используется нулевое значение соответствующего типа. Для первого столбца TIMESTAMP в таблице в качестве значения по умолчанию используются текущая дата и время. Пример create table date_zero (dt datetime not null, d date not null, t time not null, y year not null, ts timestamp not null) insert date_zero values (); select * from date_zero; -- 0000-00-00 00:00:00 | 0000-00-00 | 00:00:00 | 0000 | 2006-12-19 18:24:49 insert date_zero values ('20060229150000','20060229','900:15:20','0321','19000101140000'); select * from date_zero; -- 0000-00-00 00:00:00 | 0000-00-00 | 838:59:59 | 0000 | 0000-00-00 00:00:00 Решение Замените нулевые значения даты датой «01 января 1753 г.». Примените другой метод для сохранения нулевых дат с использованием строкового или числового типа данных. Проблема: нули в значениях года, месяца и числа В MySQL можно сохранять даты с нулевым значением года, месяца и числа в столбце DATE или DATETIME. Пример create table date_zeropart (d datetime null); insert date_zeropart values © Корпорация Майкрософт (Microsoft Corporation), 2008. Руководство по переходу с MySQL на SQL Server 2005 12 ('00001215'),('20060015'),('20061200'),('20060000'),('20060229'); -- 0000-12-15 00:00:00 -- 2006-00-15 00:00:00 -- 2006-12-00 00:00:00 -- 2006-00-00 00:00:00 -- 0000-00-00 00:00:00 Решение Используйте строковые или числовые типы данных для хранения этих значений. Проблема: недопустимые даты MySQL принимает недопустимые даты в режиме ALLOW_INVALID_DATES SQL. В режиме In ALLOW_INVALID_DATES MySQL лишь проверяет, находится ли значение месяца в диапазоне от 0 до 12, а числа — от 0 до 31. Пример create table date_inval (d datetime null); set sql_mode='ALLOW_INVALID_DATES'; insert date_inval values ('20061131'); insert date_inval values ('20061132'); set sql_mode=''; insert date_inval values ('20061131'); select * from date_inval; -- 2006-11-31 00:00:00 -- 0000-00-00 00:00:00 -- 0000-00-00 00:00:00 Решение Используйте строковые или числовые типы данных для хранения этих значений. Проблема: поддерживаемый диапазон типа данных DATETIME В MySQL тип данных DATETIME допускает значения от '0000-00-00 00:00:00' до '9999-12-31 23:59:59'. Пример create table datetime_range (d datetime); insert datetime_range values ('0000-00-00 00:00:01'); © Корпорация Майкрософт (Microsoft Corporation), 2008. Руководство по переходу с MySQL на SQL Server 2005 13 insert datetime_range values ('0000-02-28 23:00:01'); insert datetime_range values ('0170-04-30 08:05:01'); insert datetime_range values ('9999-12-31 23:59:59'); Решение Используйте строковые или числовые типы данных для хранения этих значений. Проблема: типы данных MySQL YEAR, DATE, TIME В MySQL поддерживаются типы данных YEAR, DATE и TIME, отсутствующие в SQL Server. Тип данных YEAR допускает значения в диапазоне от 1901 до 2155. Недопустимые значения YEAR преобразуются в 0000. Тип данных DATE допускает значения от '0000-00-00' до '9999-12-31'. Недопустимые значения DATE преобразуются в '0000-00-00'. Тип данных TIME допускает значения в диапазоне от '-838:59:59' до '838:59:59'. По умолчанию значения TIME, которые выходят за пределы диапазона, но являются корректными, усекаются до ближайшего граничного значения. Например, значения '-850:00:00' и '850:00:00' преобразуются в '-838:59:59' и '838:59:59'. Недопустимые значения TIME преобразуются в '00:00:00'. Пример create table time_range (t time); insert time_range values ('2 01:30:54'); -- 49:30:54 insert time_range values ('201:03:45'); -- 201:03:45 insert time_range values ('900:42:14'); -- 838:59:59 insert time_range values ('-900:42:14'); -- -838:59:59 insert time_range values ('-1 05:15:20'); -- -29:15:20 create table year2 (y year(2)); insert year2 values (20),(1920),(80),(2080); select * from year2; -- 20 20 80 80 create table year4 (y year); insert year4 select y from year2; select * from year4; -- 2020 1920 1980 2080 Решение Используйте строковые или числовые типы данных для хранения этих значений. © Корпорация Майкрософт (Microsoft Corporation), 2008. Руководство по переходу с MySQL на SQL Server 2005 14 Проблема: типы данных TIMESTAMP и DATETIME Тип данных TIMESTAMP идентичен типу данных DATETIME, могут возникать дублирующиеся значения. Пример create table table_ts ( id int auto_increment not null, d datetime null, t timestamp not null default current_timestamp on update urrent_timestamp, key(id)); insert table_ts (d) values (now()),(now()),(now()),(now()),(now()); select t, count(*) from table_ts group by t -- 2006-12-22 19:20:38 | 5 Решение Для эмуляции типа данных TIMESTAMP можно использовать триггер при событиях INSERT и UPDATE, сохраняющий текущие дату и время в поле datetime. Строковые типы Проблема: максимальный размер VARCHAR и VARBINARY В MySQL для типов данных VARCHAR и VARBINARY используется максимальный размер 65535. Данные MySQL типа VARCHAR, длина которых превышает 65535, преобразуются в данные MEDIUMTEXT или LONGTEXT. Данные MySQL типа VARBINARY, длина которых превышает 65535, преобразуются в данные MEDIUMBLOB или LONGBLOB. Пример create table t_varchar (v varchar(65532)); describe t_varchar; -- v varchar(65532) ... create table t_varchar (v varchar(65536)); describe t_varchar; -- v mediumtext ... create table t_varbinary (v varbinary(65532)); describe t_varbinary; -- v varbinary(65532) ... create table t_varbinary (v varbinary(65536)); describe t_varbinary; -- v mediumblob ... © Корпорация Майкрософт (Microsoft Corporation), 2008. Руководство по переходу с MySQL на SQL Server 2005 15 Решение Для сохранения символьных и двоичных данных длиннее 8000 байт используйте типы данных varchar(max) и varbinary(max). Проблема: BINARY attribute for fields with CHAR and VARCHAR data types Из-за атрибута MySQL BINARY происходит двоичное сличение для используемого набора символов столбца. Например, CHAR(5) BINARY в MySQL обрабатывается как CHAR(5) CHARACTER SET latin1 COLLATE latin1_bin, если по умолчанию используется набор символов latin1. Пример create table char_binary_ci (v varchar(8)); insert char_binary_ci values ('a'),('A'),('C'),('B'); select * from char_binary_ci order by v; -- 'a' 'A' 'B' 'C' create table char_binary_cs (v varchar(8) binary); insert char_binary_cs values ('a'),('A'),('C'),('B'); select * from char_binary_cs order by v; -- 'A' 'B' 'C' 'a' Решение Решения пока нет. Проблема: типы данных CHAR, VARCHAR и TEXT могут включать кодировки Юникода В MySQL имеются две кодировки Юникода: ucs2 (UCS-2 Unicode) и utf8 (UTF-8 Unicode). В SQL Server имеются типы данных nchar и nvarchar для хранения данных в кодировке Юникода и использования кодировки Юникода UCS-2. Пример create table unicode_ucs2 (v varchar(10) character set ucs2); create table unicode_utf8 (v varchar(10) character set utf8); create table collation_cp (v varchar(10) charset cp1251); insert unicode_ucs2 values ('Ïðèâåò!'); insert unicode_ucs2 values ('您好您'); insert unicode_utf8 values ('Ïðèâåò!'); insert unicode_utf8 values ('您好您'); insert collation_cp values ('Ïðèâåò!'); select length(v) from unicode_ucs2; -- 14 6 select length(v) from unicode_utf8; -- 13 9 © Корпорация Майкрософт (Microsoft Corporation), 2008. Руководство по переходу с MySQL на SQL Server 2005 16 select length(v) from collation_cp; -- 7 select char_length(v) from unicode_ucs2; -- 7 3 select char_length(v) from unicode_utf8; -- 7 3 select char_length(v) from collation_cp; -- 7 Решение Преобразуйте типы данных CHAR, VARCHAR и TEXT в кодировке Юникода в типы данных SQL Server nchar и nvarchar. Проблема: допускается индексирование типов данных BLOB и TEXT Для индексов столбцов BLOB и TEXT в MySQL нужно указать длину префикса индекса. Длина префикса может составлять до 1000 байт (767 байт для таблиц InnoDB). Пример create table blob_index (blob_col blob, index(blob_col(20))); Решение Решения пока нет. Проблема: строковые константы могут содержать ESCAPE-последовательности Каждая ESCAPE-последовательность в строковой константе начинается с обратной косой черты («\») в MySQL. Пример select 'This is \'Quoted string\''; select 'This is ''Quoted string'''; -- This is 'Quoted string' Решение Решения пока нет. Типы данных ENUM и SET Проблема: тип данных ENUM (перечисление) В MySQL поддерживается тип данных ENUM (перечисление). ENUM представляет собой строковый объект, значение которого выбрано из списка допустимых значений, пронумерованных явным образом в спецификации столбца во время создания таблицы. Если вставить недопустимое значение в столбец типа ENUM, то вставляется пустая строка в качестве специального значения ошибки. Если столбец ENUM объявлен как допускающий значение NULL, значение NULL является для него корректным и используется по умолчанию. © Корпорация Майкрософт (Microsoft Corporation), 2008. Руководство по переходу с MySQL на SQL Server 2005 17 Если столбец ENUM объявлен как NOT NULL, значением по умолчанию для него является первый элемент из списка допустимых значений. Каждое значение перечислимого типа имеет индекс. Значения ENUM сортируются в соответствии с порядком, в котором элементы перечисления указаны в спецификации столбца. Пример create table table_enum (e enum ('a','b','c') not null); insert into table_enum values ('a'); insert into table_enum values ('d'); insert into table_enum values ('a,c'); insert into table_enum values ('b,b,b'); insert into table_enum values ('b'); insert into table_enum values (); select * from table_enum; -- 'a','','','','b','a'; select * from table_enum where e=1 -- 'a', 'a' -------------------------------------------------- create procedure proc_enum (e enum ('a','b','c')) begin if e!='' then select e; else select 'Invalid argument'; end if; end call proc_enum ('a'); -- 'a' call proc_enum ('t'); -- 'Invalid argument' Решение Попробуйте эмулировать тип данных ENUM в виде таблицы подстановок, как в следующем примере кода: create table someenumtype (_id integer, _value varchar(max)). Исходная таблица будет ссылаться на эту хэш-таблицу по идентификатору _id. Необходимо добавить операторы присоединения ко всем запросам, где используется значение поля ENUM. © Корпорация Майкрософт (Microsoft Corporation), 2008. Руководство по переходу с MySQL на SQL Server 2005 18 Проблема: тип данных SET В MySQL поддерживается тип данных SET. SET представляет собой строковый объект, который может принимать различные значения, включая нулевое, каждое из которых должно выбираться из списка допустимых значений, указанного при создании таблицы. Если для столбца SET задано неподдерживаемое значение, это значение игнорируется. В MySQL значения SET сохраняются в числовом виде, причем младший разряд сохраненного значения соответствует первому компоненту набора. Пример create table table_set (s set('a','b','c') not null); insert into table_set values ('a'); insert into table_set values ('d'); insert into table_set values ('a,c'); insert into table_set values ('b,b,b'); insert into table_set values ('b'); insert into table_set values (); select * from table_set; -- 'a','','a,c','b','b','' select * from table_set where s='a,c' -- 'a,c' -------------------------------------------------- create procedure proc_set (p char(1), s set ('a','b','c')) begin if find_in_set(p,s)>0 then select p; else select 'Invalid argument'; end if; end call proc_set ('a','b,c,a'); -- 'a' call proc_set ('a','b,c'); -- 'Invalid argument' Решение Тип данных SET имеет двойственную природу — целое число (до 64 бит) и строка одновременно. Каждый бит SET соответствует описанию строки. Строковое представление значения SET состоит из соответствующих строк, разделенных запятыми. © Корпорация Майкрософт (Microsoft Corporation), 2008. Руководство по переходу с MySQL на SQL Server 2005 19 При обработке данных можно использовать как целочисленные, так и строковые представления объекта SET. Внутри объект SET хранится в виде целого числа, размер зависит от количества значений SET (от 1 до 8 байт). При эмуляции типа данных SET в SQL Server за основу берется bigint, самый большой из возможных целочисленных типов данных. Чтобы сохранить строковое представление битов в значении SET и определить все возможные биты, создайте «таблицу подстановки», как показано в следующем примере: create table lookup_set( schemaname sysname not null, -- schema name tablename sysname not null, -- table name colname sysname not null, -- column name bitmask bigint not null, -- bitmask for value position int not null, -- position in list description varchar(512) not null, -- chararcter description of value constraint pk_lookup_set primary key clustered (schemaname,tablename,colname,bitmask) ) Кроме того, создайте набор UDF для поддержки операций, использующих тип данных SET. UDF Описание char_to_set Преобразовывает строковое представление в целочисленное. Примечание. char_to_set всегда работает как при отключенном «строгом режиме» или наличии слова IGNORE в конструкции INSERT или UPDATE. set_to_char Преобразовывает целочисленное представление в строковое. clean_set Удаляет недопустимые биты из целочисленного представления. Примечание. Использование clean_set может зависеть от «строгого режима» или слова IGNORE в конструкциях INSERT и UPDATE. check_set Проверяет, имеет ли данное целочисленное представление допустимое значение SET. find_in_set Эмулирует функцию MySQL FIND_IN_SET. Проверьте в функции FIND_IN_SET возможный конфликт имен со второй эмуляцией FIND_IN_SET. © Корпорация Майкрософт (Microsoft Corporation), 2008. Руководство по переходу с MySQL на SQL Server 2005 20 Другие типы данных Проблема: пространственные типы данных в MySQL В MySQL есть типы данных, соответствующие классам OpenGIS (пространственные типы данных MySQL). Пример create table spatial_type (g geometry, p point, l linestring, pg polygon, mp multipoint) Решение Решения пока нет. Неявное преобразование типов данных Проблема: неявное преобразование типов данных в MySQL Когда значение одного типа используется в контексте, для которого требуется значение другого типа, MySQL автоматически преобразует типы данных в соответствии с типом выполняемой операции. Примеры: select 100+'ABC' -- 100 select 100+'23ABC' -- 123 select concat('ABC',345,now(),50.4789) -- ABC3452006-11-08 19:00:0050.4789 drop table if exists table_date; create table table_date (d datetime, b smallint, i int(10) zerofill, f float, s varchar(64)); set @d=19980514; insert into table_date values (@d, @d, @d, @d, @d); select * from table_date; -- 1998-05-14 00:00:00 32767 0019980514 1.99805e+007 19980514 set @d=now(); insert into table_date values (@d, @d, @d, @d, @d); select * from table_date; -- 2006-11-08 19:24:25 2006 0000002006 2006 2006-11-08 19:26:27 Решение Решения пока нет. © Корпорация Майкрософт (Microsoft Corporation), 2008. Руководство по переходу с MySQL на SQL Server 2005 21 Значения типов данных по умолчанию Проблема: неявные значения DEFAULT Если в определение столбца не включено явное значение DEFAULT, значение по умолчанию в MySQL определяется следующим образом: Если столбец может принимать значение NULL, он определяется с использованием явного предложения DEFAULT NULL. Если столбец не может принимать значение NULL, он определяется в MySQL без явного предложения DEFAULT. Если в инструкции INSERT или REPLACE не указано значение для столбца, то для ввода данных MySQL обрабатывает столбец в соответствии с действующим на данный момент режимом SQL. Если не включен строгий режим SQL, столбцу в MySQL присваивается неявное значение по умолчанию для типа данных столбца. Если строгий режим включен, для транзакционных таблиц появляется ошибка и происходит откат инструкции. Для нетранзакционных таблиц ошибка появляется, но если это происходит для второй или последующей строки многострочной инструкции, предыдущие строки будут вставлены. Пример create table table_default (i int not null, d datetime not null, s varchar(64) not null, e enum ('a','b','c') not null, n int null); insert table_default values (); insert table_default values (default,default,'ABC',default,default); select * from table_default; -- 0 0000-00-00 00:00:00 a NULL -- 0 0000-00-00 00:00:00 ABC a NULL -- DEFAULT function example create table table_defaultfunc (a int not null default 1, b int not null default 2); insert table_defaultfunc values (); insert table_defaultfunc values (default(b),default(a)); select * from table_defaultfunc; -- 1 2 -- 2 1 Решение Решения пока нет. © Корпорация Майкрософт (Microsoft Corporation), 2008. Руководство по переходу с MySQL на SQL Server 2005 22 Проблемы перехода с MySQL В этом разделе описываются проблемы, которые могут возникнуть при переходе с MySQL 5 на SQL Server 2005, и способы их устранения. Операторы В этом разделе описываются различия между операторами в MySQL и SQL Server 2005. Операторы сравнения Проблема: операторы сравнения в инструкциях DML В отличие от SQL Server в MySQL допускаются операторы сравнения в инструкциях DML. Пример create table table_logic (id int not null, v varchar(64) not null, b int not null); insert table_logic values (1,'1=2',1=2); insert table_logic values (2,'1>2',1>2); insert table_logic values (3,'1<2',1<2); select * from table_logic; -- 1 1=2 0 | 2 1>2 0 | 3 1<2 1 select 1=2, 1>2, 1<2 from dual; -- 0 0 1 update table_logic set v='2=3', b=2=3 where id=3; select * from table_logic; -- 3 2=3 0 update table_logic set v='NULL IS UNKNOWN', b=NULL IS UNKNOWN where id=3; select * from table_logic; -- 3 NULL IS UNKNOWN 1 select @a is unknown, @a is null, @a is not null; -- 1 1 0 set @a=5-1=3+1 select @a -- 0 select 'a' in ('a','b','c'), 'a' not in ('a','b','c'); -- 1 0 select 1=2=0=5=0, 2>1=1<7=1<0 -- 1 0 © Корпорация Майкрософт (Microsoft Corporation), 2008. Руководство по переходу с MySQL на SQL Server 2005 23 Решение Эмулируйте операторы сравнения в инструкциях DML с помощью функции CASE. Проблема: оператор равенства <=>, не возвращающий значение NULL Этот оператор MySQL сравнивает равенство, как и оператор =, однако возвращает 1, а не NULL, если оба операнда равны NULL, и 0, а не NULL, если один операнд равен NULL. В SQL Server нет идентичного оператора. Пример select 1 <=> 1, null <=> null, 1 <=> null, @d <=> null; -- 1 1 0 1 select 1 = 1, null = null, 1 = null, @d = null; -- 1 NULL NULL NULL Решение Решения пока нет. Проблема: оператор сравнения IS [NOT] логическое_значение Этот оператор MySQL сравнивает значение с логическим значением, где логическое_значение может быть TRUE, FALSE или UNKNOWN. В SQL Server нет аналогичного оператора. Пример create table table_is_int (i int); insert table_is_int values (-1),(0),(1),(2),(3),(null); select i is true from table_is_int; -- 1 0 1 1 1 0 select i is false from table_is_int; -- 0 1 0 0 0 0 select i is unknown from table_is_int; -- 0 0 0 0 0 1 select i=0 is true from table_is_int; -- 0 1 0 0 0 0 select * from table_is_int where (i is true) is false; -- 0 NULL select 'A' is false, 'A' is true, '7A' is false, '7A' is true, now() is true; -- 1 0 0 1 1 Решение Эмулируйте этот оператор сравнения с помощью функции CASE. © Корпорация Майкрософт (Microsoft Corporation), 2008. Руководство по переходу с MySQL на SQL Server 2005 24 Проблема: дополнительные функции оператора сравнения IS NULL В MySQL поддерживаются дополнительные функции оператора сравнения IS NULL. В MySQL можно найти строку, содержащую самое последнее значение AUTO_INCREMENT, с помощью выдачи инструкции следующего вида сразу же после создания значения: SELECT * FROM tbl_name WHERE auto_col IS NULL Для столбцов DATE и DATETIME, объявленных NOT NULL, можно найти дату '0000-00-00' с помощью следующей инструкции: SELECT * FROM tbl_name WHERE date_column IS NULL Пример create table auto_inc (id int not null auto_increment, v varchar(64) not null, key(id)); insert auto_inc (v) values ('ABC'); insert auto_inc (v) values ('DEF'); insert auto_inc (v) values ('GHI'); select * from auto_inc where id is null; -- 3 'GHI' create table auto_date (d datetime not null, v varchar(64) not null); insert auto_date set v='A'; insert auto_date set v='B'; insert auto_date set v='C', d=now(); select * from auto_date where d is null; -- 0000-00-00 00:00:00 A -- 0000-00-00 00:00:00 B Решение Решения пока нет. Битовые операторы Проблема: операторы побитового сдвига В MySQL поддерживаются операторы побитового сдвига (<< и >>), отсутствующие в SQL Server. Пример create procedure bit_shift (count int) begin declare v bigint; declare i int; © Корпорация Майкрософт (Microsoft Corporation), 2008. Руководство по переходу с MySQL на SQL Server 2005 25 set v:=1; set i:=1; while i<=count do set v := v << 1; select v, i; set i := i+1; end while; end; call bit_shift (70); -- 2 1 -- 4611686018427387904 62 -- 9223372036854775807 63 -- 9223372036854775807 64 Решение Решения пока нет. Операторы присвоения Проблема: присвоение переменных в инструкциях SET В MySQL можно присваивать переменные в инструкциях SET при помощи операторов := или =. MySQL также может присваивать значение пользовательской переменной в инструкциях, отличных от SET. В этом случае для присвоения нужно использовать оператор :=, а не =, поскольку оператор = обрабатывается как оператор сравнения в инструкциях, отличных от SET. В отличие от SQL Server, если переменная назначается в инструкции MySQL SELECT, возвращается набор записей. Пример set @a=1; set @b:=2; select @a, @b, @a=@b; -- 1 2 0 select @a:=@b; -- 2 select @a, @b, @a=@b; -- 2 2 1 create table assign_var (i int not null); insert assign_var values (1),(2),(3); select @i, @i:=i from assign_var order by i; -- NULL 1 -- 1 2 © Корпорация Майкрософт (Microsoft Corporation), 2008. Руководство по переходу с MySQL на SQL Server 2005 -- 2 26 3 select @i:=i, @i from assign_var order by i; -- 1 1 -- 2 2 -- 3 3 select @i; -- 3 Решение Решения пока нет. Переменные В этом разделе описываются различия между переменными в MySQL и SQL Server 2005. Проблема: типы поддерживаемых переменных В MySQL поддерживаются два типа переменных: пользовательские переменные @var_name Локальные переменные (переменные в хранимых процедурах) DECLARE var_name[,...] type [DEFAULT value]; В MySQL для инициализации пользовательских переменных не используется инструкция DECLARE. Они инициализируются неявным образом, когда впервые задаются (с помощью инструкции SET или SELECT) или применяются. Если обратиться к переменной, которая еще не была инициализирована с помощью инструкции SET или SELECT, эта переменная будет иметь значение NULL и строковый тип. Пользовательские переменные используются для конкретного подключения. В SQL Server нет определенных переменных для конкретного подключения. Пример 1 create procedure proc () begin select @a; end set @a=100; call proc2 (); Пример 2 create procedure proc (inout par_a int) begin set par_a=200; © Корпорация Майкрософт (Microsoft Corporation), 2008. Руководство по переходу с MySQL на SQL Server 2005 27 end call proc2 (@b); select @b; Решение Решения пока нет. Проблема: учет регистра в пользовательских переменных В именах пользовательских переменных учитывается регистр в версиях до MySQL 5.0 и не учитывается в MySQL 5.0 и более поздних версиях. Это следует принимать во внимание при выборе сопоставления в SQL Server. Решение Решения пока нет. Проблема: значение локальных переменных по умолчанию В MySQL локальные переменные могут иметь значение по умолчанию. Пример create procedure ProcA () begin declare var_a int default 100; declare var_b varchar(8) default 'ABCDEFGHIJKLMN'; declare var_c datetime default now(); declare var_d int; select var_a, var_b, var_c, var_d; -- 100 ABCDEFGH 2006-11-08 15:05:04 (NULL) end Решение Решения пока нет. © Корпорация Майкрософт (Microsoft Corporation), 2008. Руководство по переходу с MySQL на SQL Server 2005 28 Служебные инструкции Проблема: команда DELIMITER Команда MySQL DELIMITER допускает изменение разделителя инструкций. Пример create table table_a (id int); select * from table_a; delimiter // select * from table_a// drop table table_a// Решение Решения пока нет. Проблема: команда HELP (синтаксис HELP) Команда HELP возвращает сведения из справочного руководства по MySQL. HELP 'search_string' Пример HELP 'replace' Syntax: REPLACE(str,from_str,to_str) Returns the string str with all occurrences of the string from_str replaced by the string to_str. REPLACE() performs a case-sensitive match when searching for from_str. Решение Решения пока нет. © Корпорация Майкрософт (Microsoft Corporation), 2008. Руководство по переходу с MySQL на SQL Server 2005 29 Инструкции определения данных В этом разделе поясняются различия между языками определения данных в MySQL и SQL Server 2005, а также приводятся решения для устранения проблем. В разделе описываются создание таблиц, схем и представлений, преобразование временных таблиц и другие проблемы, связанные с DDL. Предложения IF NOT EXISTS, IF EXISTS и REPLACE Проблема: предложение IF NOT EXISTS в инструкциях CREATE DATABASE, CREATE TABLE, CREATE EVENT Ключевые слова IF NOT EXISTS не допускают возникновение ошибки, если таблица (база данных, событие) существует. Примечание. Если использовать IF NOT EXISTS в инструкции CREATE TABLE...SELECT, то все строки, выбранные при помощи SELECT, вставляются вне зависимости от того, существует ли таблица. Пример кода MySQL create database db_exists; create database db_exists; -- Error Code : 1007 Can't create database 'db_exists'; database exists A: create database if not exists db_exists; -- No Action create table exists_a (i int not null); create table exists_a (i int not null); -- Error Code : 1050 Table 'exists_a' already exists B: create table if not exists exists_a (i int not null, v varchar(64) null); -- No Action show create table exists_a; -- create table exists_a (i int(11) not null) C: create table if not exists exists_a select now() as d from dual; show create table exists_a; -- create table exists_a (i int(11) not null) select * from exists_a; -- 2007 Решение CREATE DATABASE Замените предложение IF NOT EXISTS следующим условием: IF NOT EXISTS (SELECT name FROM sys.databases © Корпорация Майкрософт (Microsoft Corporation), 2008. Руководство по переходу с MySQL на SQL Server 2005 30 WHERE name = N'<db_name>') BEGIN <create_database_statement_without_if_not_exists> END CREATE TABLE Если синтаксис CREATE TABLE...SELECT не используется, замените предложение IF NOT EXISTS следующим условием: Для постоянных таблиц: IF NOT EXISTS (SELECT * FROM sys.objects WHERE object_id = OBJECT_ID(N'<table_name>') AND type in (N'U')) BEGIN <create_table_statement_without_if_not_exists> END Для временных таблиц: IF NOT EXISTS (SELECT * FROM tempdb.sys.objects WHERE object_id = OBJECT_ID(N'tempdb..<#table_name>') AND type in (N'U')) BEGIN <create_#table_statement_without_if_not_exists> END Если используется синтаксис CREATE TABLE...SELECT, замените предложение IF NOT EXISTS следующим условием: Для постоянных таблиц: IF NOT EXISTS (SELECT * FROM sys.objects WHERE object_id = OBJECT_ID(N'<table_name>') AND type in (N'U')) BEGIN <create_table_statement_without_if_not_exists> <insert_select_statement> END ELSE BEGIN <insert_select_statement> END Для временных таблиц: IF NOT EXISTS (SELECT * FROM tempdb.sys.objects WHERE object_id = OBJECT_ID(N'tempdb..<#table_name>') AND type in (N'U')) BEGIN © Корпорация Майкрософт (Microsoft Corporation), 2008. Руководство по переходу с MySQL на SQL Server 2005 31 <create_#table_statement_without_if_not_exists> <insert_select_statement> END ELSE BEGIN <insert_select_statement> END CREATE EVENT Решения пока нет. Пример кода SQL Server A: IF NOT EXISTS (SELECT name FROM sys.databases WHERE name = N'db_exists') BEGIN create database db_exists END B: IF NOT EXISTS (SELECT * FROM sys.objects WHERE object_id = OBJECT_ID(N'exists_a') AND type in (N'U')) BEGIN create table exists_a (i int not null, v varchar(64) null) END C: IF NOT EXISTS (SELECT * FROM sys.objects WHERE object_id = OBJECT_ID(N'exists_a') AND type in (N'U')) BEGIN create table exists_a (d datetime not null default '1753-01-01 00:00:00') insert exists_a select getdate() as d END ELSE BEGIN insert exists_a select getdate() as d END Проблема: предложение IF EXISTS в инструкциях DROP DATABASE, DROP TABLE, DROP VIEW, DROP EVENT, DROP PROCEDURE, DROP FUNCTION Ключевые слова IF EXISTS не допускают возникновение ошибки, если база данных (таблица, представление, событие, процедура, функция) не существует. © Корпорация Майкрософт (Microsoft Corporation), 2008. Руководство по переходу с MySQL на SQL Server 2005 32 Пример кода MySQL A: drop database if exists db_exists; drop database db_exists; -- Error Code : 1008 Can't drop database 'db_exists'; database doesn't exist B: drop table if exists exists_a; C: drop view if exists exists_view; D: drop procedure if exists exists_proc; E: drop function if exists exists_func; Решение DROP DATABASE. Замените предложение IF EXISTS следующим условием: IF EXISTS (SELECT name FROM sys.databases WHERE name = N'<db_name>') BEGIN <drop_database_statement_without_if_exists> END DROP TABLE. Замените предложение IF EXISTS следующим условием: IF EXISTS (SELECT * FROM tempdb.sys.objects WHERE object_id = OBJECT_ID(N'tempdb..<#table_name>') AND type in (N'U')) BEGIN <drop_#table_statement_without_if_exists> END ELSE BEGIN IF EXISTS (SELECT * FROM sys.objects WHERE object_id = OBJECT_ID(N'<table_name>') AND type in (N'U')) BEGIN <drop_table_statement_without_if_exists> END DROP TEMPORARY TABLE. Замените предложение IF EXISTS следующим условием: IF EXISTS (SELECT * FROM tempdb.sys.objects WHERE object_id = OBJECT_ID(N'tempdb..<#table_name>') AND type in (N'U')) BEGIN <drop_#table_statement_without_if_exists> END © Корпорация Майкрософт (Microsoft Corporation), 2008. Руководство по переходу с MySQL на SQL Server 2005 33 DROP VIEW. Замените предложение IF EXISTS следующим условием: IF EXISTS (SELECT * FROM sys.views WHERE object_id = OBJECT_ID(N'<view_name>')) BEGIN <drop_view_statement_without_if_exists> END DROP EVENT. Решения пока нет. DROP PROCEDURE. Замените предложение IF EXISTS следующим условием: IF EXISTS (SELECT * FROM sys.objects WHERE object_id = OBJECT_ID(N'<proc_name>') AND type in (N'P', N'PC')) BEGIN <drop_procedure_statement_without_if_exists> END DROP FUNCTION. Замените предложение IF EXISTS следующим условием: IF EXISTS (SELECT * FROM sys.objects WHERE object_id = OBJECT_ID(N'<func_name>') AND type in (N'FN', N'IF', N'TF', N'FS', N'FT')) BEGIN <drop_function_statement_without_if_exists> END Пример кода SQL Server A: IF EXISTS (SELECT name FROM sys.databases WHERE name = N'db_exists') BEGIN drop database db_exists END B: IF EXISTS (SELECT * FROM sys.objects WHERE object_id = OBJECT_ID(N'exists_a') AND type in (N'U')) BEGIN drop table exists_a END © Корпорация Майкрософт (Microsoft Corporation), 2008. Руководство по переходу с MySQL на SQL Server 2005 34 C: IF EXISTS (SELECT * FROM sys.views WHERE object_id = OBJECT_ID(N'exists_view')) BEGIN drop view exists_view END D: IF EXISTS (SELECT * FROM sys.objects WHERE object_id = OBJECT_ID(N'<exists_proc>') AND type in (N'P', N'PC')) BEGIN drop procedure exists_proc END E: IF EXISTS (SELECT * FROM sys.objects WHERE object_id = OBJECT_ID(N'exists_func') AND type in (N'FN', N'IF', N'TF', N'FS', N'FT')) BEGIN drop function exists_func; END Проблема: предложение OR REPLACE в инструкциях CREATE VIEW Предложение MySQL OR REPLACE заменяет существующее представление. Пример кода MySQL create or replace view repl_view as select now(); create or replace view repl_view as select version(); Решение Замените предложение OR REPLACE следующим условием: IF EXISTS (SELECT * FROM sys.views WHERE object_id = OBJECT_ID(N'<view_name>')) BEGIN <drop_view_statement> exec dbo.sp_executesql @statement = N'<create_view_statement_without_or_replace>' END ELSE BEGIN © Корпорация Майкрософт (Microsoft Corporation), 2008. Руководство по переходу с MySQL на SQL Server 2005 35 exec dbo.sp_executesql @statement = N'<create_view_statement_without_or_replace>' END Пример кода SQL Server IF EXISTS (SELECT * FROM sys.views WHERE object_id = OBJECT_ID(N'repl_view')) BEGIN drop view repl_view exec dbo.sp_executesql @statement = N'create view repl_view as select getdate() as d' END ELSE BEGIN exec dbo.sp_executesql @statement = N'create view repl_view as select getdate() as d' END Временные таблицы Проблема: таблицы MySQL TEMPORARY не удаляются при выходе из области действия Временные таблицы SQL Server, созданные с помощью инструкции CREATE TEMPORARY TABLE, видны только текущему подключению и удаляются автоматически после закрытия подключения. Но временные таблицы MySQL не удаляются при выходе из области действия. В SQL Server: Локальная временная таблица, создаваемая в хранимой процедуре, удаляется автоматически при завершении этой процедуры. К таблице могут обращаться любые вложенные хранимые процедуры, выполняемые хранимой процедурой, в которой создана таблица. К таблице не может обращаться процесс, вызываемый хранимой процедурой, в которой создана таблица. Имя локальной временной таблицы, создаваемой в хранимой процедуре или триггере, может совпадать с именем временной таблицы, которая была создана до вызова хранимой процедуры или триггера. Однако если в запросе указывается временная таблица и одновременно существуют две временные таблицы с одним именем, невозможно определить, какая таблица будет выбрана по запросу. Во вложенной хранимой процедуре может также создаваться временная таблица, имя которой совпадает с именем временной таблицы, которая была создана вызывающей ее хранимой процедурой. Однако для разрешения модификаций в таблице, созданной во вложенной процедуре, таблица должна иметь ту же структуру и те же имена столбцов, что и таблица, созданная в вызывающей процедуре. © Корпорация Майкрософт (Microsoft Corporation), 2008. Руководство по переходу с MySQL на SQL Server 2005 36 Пример 1 кода MySQL create procedure proctemptable () begin create temporary table table_temp (d datetime); insert table_temp values (now()); end call proctemptable(); select * from table_temp; -- 2006-11-20 11:18:58 call proctemptable(); -- Error Code : 1050 Table 'table_temp' already exists Пример 2 кода MySQL create procedure test2 () begin create temporary table t (x int); insert into t values (2); select x as test2col from t; end create procedure test1 () begin create temporary table t (x int); insert into t values (1); select x as test1col from t; call test2 (); end call test1 (); -- test1col = 1 (1 row) -- ERROR 1050 (42S01): Table 't' alredy exists Решение Решения пока нет. © Корпорация Майкрософт (Microsoft Corporation), 2008. Руководство по переходу с MySQL на SQL Server 2005 37 Проблема: ключевое слово TEMPORARY в инструкциях CREATE TABLE и DROP TABLE В MySQL ключевое слово TEMPORARY в инструкции CREATE TABLE служит для создания временной таблицы. В MySQL ключевое слово TEMPORARY в инструкции DROP TABLE служит для удаления временной таблицы. Пример кода MySQL A: create temporary table temp_table_a (a int not null); B: create temporary table if not exists atest.temp_table_b (b int not null); C: drop temporary table temp_table_a; D: drop temporary table if exists atest.temp_table_b; Решение Замените ключевое слово TEMPORARY знаком решетки (#) перед именем таблицы. Имя базы данных опустите. Пример кода SQL Server A: create table #temp_table_a (a int not null) B: IF NOT EXISTS (SELECT * FROM tempdb.sys.objects WHERE object_id = OBJECT_ID(N'tempdb..#temp_table_b') AND type in (N'U')) BEGIN create table #temp_table_b (b int not null) END C: drop table #temp_table_a; D: IF EXISTS (SELECT * FROM tempdb.sys.objects WHERE object_id = OBJECT_ID(N'tempdb..#temp_table_b') AND type in (N'U')) BEGIN drop table #temp_table_b; END Проблема: скрытие таблиц Если в MySQL временная таблица создается с тем же именем, как и существующая постоянная таблица, то постоянная таблица скрывается до удаления временной таблицы. © Корпорация Майкрософт (Microsoft Corporation), 2008. Руководство по переходу с MySQL на SQL Server 2005 38 Пример 1 кода MySQL create table permanent_temp (v varchar(4) not null, d datetime not null); insert permanent_temp values ('ABCD',now()); select * from permanent_temp; -- 'ABCD' '2007-02-08 16:19:40' create temporary table permanent_temp (i int not null); insert permanent_temp values (1); select * from permanent_temp; -- 1 drop table permanent_temp; -- drop temporary table select * from permanent_temp; -- 'ABCD' '2007-02-08 16:19:40' drop table permanent_temp; -- drop permanent table Пример 2 кода MySQL create table permanent_temp (i int not null); insert permanent_temp values (0); select * from permanent_temp; -- 0 create temporary table permanent_temp (i int not null); insert permanent_temp values (1); select * from permanent_temp; -- 1 drop temporary table permanent_temp; -- drop temporary table select * from permanent_temp; -- 0 drop temporary table permanent_temp; -- Error Code : 1051 Unknown table 'permanent_temp' drop table permanent_temp; -- drop permanent table Решение Решения пока нет. Проблема: временные таблицы в функциях В MySQL можно использовать временные таблицы в функциях. В SQL Server это не допускается. Пример кода MySQL create function temp_func_sum () returns double begin declare s double; select sum(i) into s from temp_func; return s; © Корпорация Майкрософт (Microsoft Corporation), 2008. Руководство по переходу с MySQL на SQL Server 2005 39 end session 1: create temporary table temp_func (i int not null); insert into temp_func values (1),(2),(3),(4); select temp_func_sum(); -- 10 session 2: create temporary table temp_func (i numeric(19,9) not null); insert into temp_func values (1.1000),(1.0100),(1.0010),(1.0001); select temp_func_sum(); -- 4.1111 session 3: create temporary table temp_func (i varchar(4) not null); insert into temp_func values ('ABCD'),('5A'),('3.14P'),('1') select temp_func_sum(); -- 9.14 Решение Решения пока нет. Ключевое слово SCHEMA в инструкциях DATABASE Проблема: ключевое слово SCHEMA в инструкциях DATABASES В инструкциях DATABASE (ALTER, CREATE, DROP, RENAME) ключевое слово SCHEMA можно использовать как синоним ключевого слова DATABASE. Решение Замените ключевое слово SCHEMA на ключевое слово DATABASE. Предложения CHARACTER SET и COLLATE в инструкциях DDL Проблема: предложения CHARACTER SET и COLLATE в инструкциях DDL В терминологии MySQL набор символов — это набор символов и кодов, а сличение — это набор правил для сравнения символов в наборе. Понятие сличение в SQL Server включает оба эти значения. © Корпорация Майкрософт (Microsoft Corporation), 2008. Руководство по переходу с MySQL на SQL Server 2005 40 Пример кода MySQL create database db_a character set latin1 collate latin1_swedish_ci; Решение Преобразуйте предложения MySQL CHARACTER SET и COLLATE в предложение SQL Server COLLATE. Проблема: предложения CONVERT TO CHARACTER SET и [DEFAULT] CHARACTER SET в инструкциях ALTER TABLE В отличие от MySQL в SQL Server не поддерживаются изменения сличения на уровне таблиц. Решение Решения пока нет. Инструкция CREATE INDEX Проблема: значения NULL в индексах UNIQUE Индекс MySQL UNIQUE допускает несколько значений NULL в столбцах, которые могут содержать значения NULL. Пример кода MySQL create table tabindex_b (i int null); create unique index idx_tabindex_b on tabindex_b (i); insert tabindex_b values (1); insert tabindex_b values (2); insert tabindex_b values (3); insert tabindex_b values (1); -- Duplicate entry '1' for key 1 insert tabindex_b values (null); -- 1 row(s) affected insert tabindex_b values (null); -- 1 row(s) affected Решение Решения пока нет. © Корпорация Майкрософт (Microsoft Corporation), 2008. Руководство по переходу с MySQL на SQL Server 2005 41 Проблема: индексы FULLTEXT MySQL может создавать индексы FULLTEXT в таблицах, не имеющих уникального индекса ключа. MySQL может создавать более одного индекса FULLTEXT в таблице. Параметр WITH PARSER можно использовать только вместе с индексами FULLTEXT. Он связывает подключаемый модуль синтаксического анализа с индексом, если необходима специальная обработка полнотекстовых операций индексации и поиска. Пример кода MySQL create table tabindex_full (i int not null, t varchar(2048) null) engine = myisam; create unique index unique_tabindex_full on tabindex_full (i); create fulltext index full_tabindex_full on tabindex_full (t); Решение Индексы FULLTEXT можно преобразовывать при выполнении всех перечисленных ниже условий: для таблицы существует только один индекс FULLTEXT; в таблице есть индекс для применения ключа полнотекстового поиска; индекс FULLTEXT не использует подключаемый модуль анализатора. Пример кода SQL Server create table tabindex_full (i int not null, t varchar(2048) null) create unique index unique_tabindex_full on tabindex_full (i) create fulltext index on tabindex_full (t) key index unique_tabindex_full Проблема: индексы SPATIAL Индексы SPATIAL поддерживаются только для таблиц MyISAM и могут включать только пространственные столбцы, определенные как NOT NULL. Решение Решения пока нет. Проблема: длина префикса индекса Для столбцов CHAR, VARCHAR, BINARY и VARBINARY можно создавать индексы с использованием только главной части значений столбца; при этом с помощью синтаксиса имя_столбца(длина) указывается длина префикса индекса. Столбцы типа BLOB и TEXT тоже можно индексировать, но для них необходимо указывать длину префикса. Длина префикса указывается в символах для недвоичных строковых типов и в байтах для двоичных строковых типов. Таким образом, элементы индекса состоят из указанного числа первых символов каждого значения столбца для типов CHAR, VARCHAR и TEXT и указанного числа первых байт каждого значения столбца для типов BINARY, VARBINARY и BLOB. © Корпорация Майкрософт (Microsoft Corporation), 2008. Руководство по переходу с MySQL на SQL Server 2005 42 Пример кода SQL Server create table tabindex_text (t text not null); create unique index unique_tabindex_text on tabindex_text (t(4)); insert tabindex_text values ('ABCDEFG'); insert tabindex_text values ('ABCEFGD'); insert tabindex_text values ('ABKLMND'); insert tabindex_text values ('ABCKLMN'); insert tabindex_text values ('ABCDLMN'); -- Duplicate entry 'ABCD' for key 1 create table tabindex_blob (b blob null); create unique index unique_tabindex_blob on tabindex_blob (b(4)); insert tabindex_blob values (0x01020304050607); insert tabindex_blob values (0x01020310111204); insert tabindex_blob values (0x01020304101112); -- Duplicate entry ' ' for key 1 Решение Добавьте в таблицу вычисленные столбцы, эмулирующие длину префикса индекса. Создайте индекс по вычисленным столбцам. Пример кода SQL Server create table tabindex_text (t varchar(max) not null, t_comp as convert(varchar(4),t)) create unique index unique_tabindex_text on tabindex_text (t_comp) insert tabindex_text values ('ABCDEFG') insert tabindex_text values ('ABCEFGD') insert tabindex_text values ('ABKLMND') insert tabindex_text values ('ABCKLMN') insert tabindex_text values ('ABCDLMN') -- Cannot insert duplicate key row ... create table tabindex_blob (b varbinary(max) null, b_comp as convert(varbinary(4),b)); create unique index unique_tabindex_blob on tabindex_blob (b_comp); insert tabindex_blob values (0x01020304050607); insert tabindex_blob values (0x01020310111204); insert tabindex_blob values (0x01020304101112); -- Cannot insert duplicate key row ... © Корпорация Майкрософт (Microsoft Corporation), 2008. Руководство по переходу с MySQL на SQL Server 2005 43 Инструкция CREATE TABLE Проблема: имена CONSTRAINT В MySQL можно опускать имена ограничений. В MySQL разрешены повторяющиеся имена ограничений. Пример кода MySQL create table tab_constr ( id int not null, d datetime, fk int not null, constraint primary key (id), constraint unique (d), constraint foreign key (fk) references tab_a (i) ); create table tab_constr_dub ( id int not null, d datetime, constraint key_tab_constr_dub primary key (id), constraint key_tab_constr_dub unique (d) ); Решение Создавайте допустимые уникальные имена ограничений. Пример кода SQL Server create table tab_constr ( id int not null, d datetime, fk int not null, constraint pk_tab_constr primary key (id), constraint uq_tab_constr unique (d), constraint fk_tab_constr foreign key (fk) references tab_a (i) ); create table tab_constr_dub ( id int not null, d datetime, constraint pk_tab_constr_dub primary key (id), constraint uq_tab_constr_dub unique (d) ); © Корпорация Майкрософт (Microsoft Corporation), 2008. Руководство по переходу с MySQL на SQL Server 2005 44 Проблема: определения индексов В MySQL можно определять индексы в тексте объявления таблицы. Пример кода MySQL create table tab_index ( i int not null, n int not null, d datetime null, v varchar(2048) not null, primary key (i), index idx_tab_index (n), key (d), fulltext index ft_tab_index (v)) engine = myisam; Решение Удалите объявления индексов из объявлений таблиц. Преобразуйте их в отдельные инструкции CREATE INDEX. Создавайте допустимые уникальные имена индексов. Замените ключевое слово KEY на ключевое слово INDEX. Пример кода SQL Server create table tab_index ( i int not null, n int not null, d datetime null, v varchar(2048) not null, primary key (i)) create index idx_tab_index on tab_index (n) create index key_tab_index on tab_index (d) create fulltext index on tab_index (v) key index pk__tab_index__72e607db Проблема: индексы ограничений FOREIGN KEY В отличие от SQL Server в MySQL для ограничений FOREIGN KEY индекс создается автоматически. Решение Решения пока нет. © Корпорация Майкрософт (Microsoft Corporation), 2008. Руководство по переходу с MySQL на SQL Server 2005 45 Проблема: ключевое слово RESTRICT в параметрах ссылок В MySQL поддерживается ключевое слово RESTRICT в параметрах ссылок. Решение Замените ключевое слово RESTRICT в параметрах ссылок на ключевое слово NO ACTION. Проблема: ключевое слово KEY в определениях столбцов В MySQL поддерживается ключевое слово KEY в определениях столбцов. Решение Замените ключевое слово KEY в определениях столбцов на ключевое слово PRIMARY KEY. Проблема: параметр столбца AUTO_INCREMENT и параметр таблицы AUTO_INCREMENT В MySQL поддерживаются параметр столбца AUTO_INCREMENT и параметр таблицы AUTO_INCREMENT. Пример кода MySQL create table auto_a ( i int not null auto_increment primary key, d datetime null ) auto_increment = 1000; insert auto_a values (null,now()),(null,now()),(null,now()); select * from auto_a; -- 1000 2007-02-16 17:41:43 -- 1001 2007-02-16 17:41:43 -- 1002 2007-02-16 17:41:43 Решение Вместо параметра столбца MySQL AUTO_INCREMENT следует использовать свойство столбца SQL Server IDENTITY. Параметр таблицы MySQL AUTO_INCREMENT следует преобразовать в SQL Server как параметр seed свойства IDENTITY. Пример кода SQL Server create table auto_a ( i int not null identity(1000,1) primary key, d datetime null © Корпорация Майкрософт (Microsoft Corporation), 2008. Руководство по переходу с MySQL на SQL Server 2005 46 ) insert auto_a values (getdate()) insert auto_a values (getdate()) insert auto_a values (getdate()) Проблема: таблицы MERGE Таблицы MERGE (система хранения) представляют собой набор идентичных таблиц MyISAM, которые можно использовать как одну. Пример кода MySQL create table merge_a (a int not null) engine=myisam ; create table merge_b (b int not null) engine=myisam; create table merge_m (m int not null) engine=merge union=(merge_a,merge_b); insert merge_a values (1),(2),(3); insert merge_b values (4),(5),(6),(7); select * from merge_a; -- 1 2 3 select * from merge_b; -- 4 5 6 7 select * from merge_m; -- 1 2 3 4 5 6 7 update merge_m set m=m+10 where m % 2 = 0; select * from merge_a; -- 1 12 3 select * from merge_b; -- 14 5 16 7 Решение Решения пока нет. Проблема: синтаксис CREATE TABLE...SELECT В MySQL можно создавать одну таблицу из другой, добавляя инструкцию SELECT в конце инструкции CREATE TABLE. MySQL создает новые столбцы для всех элементов в списке SELECT с уникальными именами. Инструкция CREATE TABLE...SELECT добавляет данные результата в новую таблицу. © Корпорация Майкрософт (Microsoft Corporation), 2008. Руководство по переходу с MySQL на SQL Server 2005 47 Пример кода MySQL create table sel_a select 1 as id from dual; show create table sel_a; -- create table sel_a (id bigint not null) select * from sel_a; -- 1 create table sel_b (id bigint not null) select 2 as id from dual; show create table sel_b; -- create table sel_b (id bigint not null) select * from sel_b; -- 2 create table sel_c (id bigint not null) select 'ABC' as val, 1000 as id from dual; show create table sel_c; -- create table sel_c (val varchar(3) not null, id bigint not null) select * from sel_c; -- ABC 1000 create table sel_d (id bigint not null) select 'ABC' as val, 1000 from dual; show create table sel_d; -- create table sel_d (id bigint not null, val varchar(3) not null, `1000` bigint not null) select * from sel_d; -- 0 ABC 1000 Решение Решения пока нет. Проблема: синтаксис CREATE TABLE...LIKE В MySQL поддерживается LIKE для создания пустой таблицы на базе определения другой таблицы, включая все атрибуты столбцов и индексы, определенные в исходной таблице. Пример кода MySQL create table like_a ( i int not null auto_increment primary key, d datetime not null unique, v varchar(1024) null, © Корпорация Майкрософт (Microsoft Corporation), 2008. Руководство по переходу с MySQL на SQL Server 2005 48 fulltext (v) ) engine = myisam; create table like_b like like_a; show create table like_b; /* create table `like_b` ( `i` int not null auto_increment, `d` datetime not null, `v` varchar(1024) collate latin1_general_ci default null, primary key (`i`), unique key `d` (`d`), fulltext key `v` (`v`) ) engine=myisam default charset=latin1 collate=latin1_general_ci */ Решение Решения пока нет. Проблема: ссылки чужих ключей на другие базы данных В MySQL можно создавать ссылки чужих ключей на другие базы данных. Решение Решения пока нет. Инструкция ALTER TABLE Проблема: ключевое слово IGNORE Расширение IGNORE определяет действия ALTER TABLE, если имеются повторяющиеся элементы в уникальных ключах новой таблицы или появляются предупреждения при включенном строгом режиме. Если ключевое слово IGNORE не указано, то при появлении ошибки повторяющегося ключа копия отменяется и выполняется откат. Если указано ключевое слово IGNORE, используется только первая строка из строк, имеющих копии в уникальном ключе. Остальные конфликтующие строки удаляются. Решение Решения пока нет. © Корпорация Майкрософт (Microsoft Corporation), 2008. Руководство по переходу с MySQL на SQL Server 2005 49 Проблема: ключевые слова FIRST | AFTER Чтобы добавить в MySQL столбец на определенном месте строки таблицы, используется предложение FIRST или AFTER имя_столбца. По умолчанию столбец добавляется последним. Также можно использовать предложения FIRST и AFTER в операциях CHANGE и MODIFY со столбцами. Решение Решения пока нет. Проблема: предложение ALTER ALTER...SET DEFAULT и ALTER...DROP DEFAULT соответственно указывают новое значение по умолчанию для столбца или удаляют старое значение по умолчанию. Пример кода MySQL create table alter_def (i int not null); insert alter_def values (); alter table alter_def alter i set default 99; insert alter_def values (); alter table alter_def alter i drop default; insert alter_def values (); select * from alter_def; -- 0 99 0 Решение Преобразуйте предложения ALTER...SET DEFAULT и ALTER...DROP DEFAULT в предложения ограничений ADD/DROP DEFAULT. Пример кода SQL Server create table alter_def (i int not null); insert alter_def default values; -- Cannot insert the value NULL into 'i' alter table alter_def add constraint i_def default 99 for i; insert alter_def default values; alter table alter_def drop constraint i_def; insert alter_def default values; -- Cannot insert the value NULL into 'i' select * from alter_def; -- 99 Проблема: предложение CHANGE MySQL поддерживает предложение CHANGE в инструкциях ALTER TABLE. © Корпорация Майкрософт (Microsoft Corporation), 2008. Руководство по переходу с MySQL на SQL Server 2005 50 Пример кода MySQL create table change_a (i int not null); alter table change_a change i i varchar(64) null; show create table change_a; alter table change_a change i d datetime not null; show create table change_a; Решение Предложение CHANGE преобразуется в предложение SQL Server ALTER COLUMN. Если CHANGE переименовывает столбец, используйте дополнительный вызов sp_rename. Пример кода SQL Server create table change_a (i int not null); alter table change_a alter column i varchar(64) null; sp_help 'change_a'; alter table change_a alter column i datetime not null; exec sp_rename 'change_a.i', 'd', 'COLUMN'; sp_help 'change_a'; Проблема: предложение MODIFY MySQL поддерживает предложение MODIFY в инструкциях ALTER TABLE. Пример кода MySQL create table modify_a (i int not null); alter table modify_a modify i varchar(64) null; show create table modify_a; alter table modify_a modify i datetime not null; show create table modify_a; Решение Преобразуйте предложение MODIFY в предложение ALTER COLUMN. Пример кода SQL Server create table modify_a (i int not null); alter table modify_a alter column i varchar(64) null; sp_help 'modify_a'; alter table modify_a alter column i datetime not null; sp_help 'modify_a'; © Корпорация Майкрософт (Microsoft Corporation), 2008. Руководство по переходу с MySQL на SQL Server 2005 51 Проблема: предложение DROP [COLUMN] имя столбца В MySQL инструкция ALTER TABLE может содержать предложение DROP с таким же значением, как у DROP COLUMN. Решение Добавьте все отсутствующие ключевые слова COLUMN. Проблема: предложение DROP PRIMARY KEY MySQL поддерживает предложение DROP PRIMARY KEY в инструкциях ALTER TABLE. Пример кода MySQL create table alter_c (id int not null, v varchar(64) not null); alter table alter_c add constraint pk_alter_c primary key (id); alter table alter_c drop primary key; Решение Предложение DROP PRIMARY KEY следует заменить на предложение DROP CONSTRAINT pk_имя_ограничения. Пример кода SQL Server create table alter_c (id int not null, v varchar(64) not null) alter table alter_c add constraint pk_alter_c primary key (id) alter table alter_c drop constraint pk_alter_c Проблема: предложение DROP {INDEX|KEY} имя_индекса MySQL поддерживает предложение DROP INDEX в инструкциях ALTER TABLE. Пример кода MySQL create table alter_i ( i int not null, n int not null, primary key (i), index idx_tab_index (n)); alter table alter_i drop index idx_tab_index; © Корпорация Майкрософт (Microsoft Corporation), 2008. Руководство по переходу с MySQL на SQL Server 2005 52 Решение Предложения DROP INDEX в инструкциях ALTER TABLE следует преобразовать в отдельные инструкции DROP INDEX. Пример кода SQL Server create table alter_i ( i int not null, n int not null, primary key (i)) create index idx_tab_index on alter_i (n) drop index alter_i.idx_tab_index Проблема: предложение DROP FOREIGN KEY MySQL поддерживает предложение DROP FOREIGN KEY в инструкциях ALTER TABLE. Пример кода MySQL create table alter_f (f_id int not null, c_id int not null); alter table alter_f add constraint fk_alter_f foreign key (c_id) references alter_c (id); alter table alter_f drop foreign key fk_alter_f; Решение Предложение DROP FOREIGN KEY следует заменить на предложение DROP CONSTRAINT fk_имя_ограничения. Пример кода SQL Server create table alter_f (f_id int not null, c_id int not null) alter table alter_f add constraint fk_alter_f foreign key (c_id) references alter_c (id) alter table alter_f drop constraint fk_alter_f Проблема: предложения DISABLE KEYS и ENABLE KEYS Предложения ALTER TABLE...DISABLE KEYS сообщают MySQL о том, что необходимо остановить обновление неуникальных индексов для таблицы MyISAM. Затем используйте предложения ALTER TABLE...ENABLE KEYS для повторного создания отсутствующих индексов. В MySQL это делается по специальному алгоритму, который работает значительно быстрее, чем поочередная вставка ключей, поэтому отключение ключей перед массовыми операциями вставки должно значительно ускорять операции. © Корпорация Майкрософт (Microsoft Corporation), 2008. Руководство по переходу с MySQL на SQL Server 2005 53 Решение Решения пока нет. Проблема: предложение RENAME MySQL поддерживает предложение RENAME в инструкциях ALTER TABLE. Пример кода MySQL create table rename_a (i int not null); insert rename_a values (1); select * from rename_a; -- 1 alter table rename_a rename to rename_b; select * from rename_a; -- Table 'rename_a' doesn't exist select * from rename_b; -- 1 Решение Преобразуйте предложение RENAME в отдельный вызов sp_rename. Пример кода SQL Server create table rename_a (i int not null); insert rename_a values (1); select * from rename_a; -- 1 exec sp_rename 'rename_a', 'rename_b'; select * from rename_a; -- Invalid object name 'rename_a' select * from rename_b; -- 1 Проблема: предложение ORDER BY В MySQL предложение ORDER BY в инструкции ALTER TABLE позвляет создавать новую таблицу с определенным порядком строк. Решение Решения пока нет. Инструкция RENAME DATABASE Проблема: инструкции RENAME DATABASE В MySQL поддерживается инструкция RENAME DATABASE. Решение Преобразуйте инструкции RENAME DATABASE в вызов sp_renamedb. © Корпорация Майкрософт (Microsoft Corporation), 2008. Руководство по переходу с MySQL на SQL Server 2005 54 Инструкция RENAME TABLE Проблема: инструкции RENAME TABLE Инструкция MySQL RENAME TABLE позволяет переименовать одну или несколько таблиц или представлений. Пример кода MySQL create table rename_a (i int not null); insert rename_a values (1); create table rename_b (d datetime not null); insert rename_b values (now()); rename tables rename_a to rename_c, rename_b to rename_a, rename_c to rename_b; select * from rename_a; -- 2007-02-20 15:03:37 select * from rename_b; -- 1 select * from rename_c; -- Table 'ATest.rename_c' doesn't exist create view rename_view_a as select 'view_string' as vs; rename table rename_view_a to rename_view_b; select * from rename_view_a; -- Table 'ATest.rename_view_a' doesn't exist select * from rename_view_b; -- 'view_string' Решение Преобразуйте каждую операцию RENAME TABLE в отдельный вызов sp_rename. Пример кода SQL Server create table rename_a (i int not null); insert rename_a values (1); create table rename_b (d datetime not null); insert rename_b values (getdate()); exec sp_rename 'rename_a', 'rename_c' exec sp_rename 'rename_b', 'rename_a' exec sp_rename 'rename_c', 'rename_b' select * from rename_a; -- 2007-02-20 15:06:48.967 select * from rename_b; -- 1 select * from rename_c; -- Invalid object name 'rename_c' © Корпорация Майкрософт (Microsoft Corporation), 2008. Руководство по переходу с MySQL на SQL Server 2005 55 create view rename_view_a as select 'view_string' as vs; exec sp_rename 'rename_view_a', 'rename_view_b'; select * from rename_view_a; -- Invalid object name 'rename_view_a' select * from rename_view_b; -- 'view_string' Проблема: перемещение таблиц между базами данных при помощи инструкции RENAME TABLE В MySQL можно использовать инструкцию RENAME TABLE для перемещения таблиц из одной базы данных в другую. Пример кода MySQL create table world.rename_table (v varchar(8) null); insert world.rename_table values ('ABC'); rename table world.rename_table to sakila.rename_table; select * from world.rename_table; -- Table 'world.rename_table' doesn't exist select * from sakila.rename_table; -- 'ABC' Решение Решения пока нет. Инструкции CREATE VIEW, ALTER VIEW, DROP VIEW Проблема: префикс имени базы данных в имени представления В отличие от MySQL в SQL Server CREATE/ALTER/DROP VIEW не позволяет указывать имя базы данных в качестве префикса имени объекта. Пример кода MySQL create view sakila.view_a as select 'ABCDE' as s; select * from sakila.view_a; -- ABCDE drop view sakila.view_a; Решение Решения пока нет. Проблема: ключевое слово LOCAL в предложении WITH CHECK OPTION В предложении WITH CHECK OPTION для обновляемого представления ключевые слова LOCAL и CASCADED определяют область действия контрольной проверки, когда представление определено на основе другого представления. Ключевое слово LOCAL ограничивает действие CHECK OPTION только определяемым представлением. При использовании ключевого слова © Корпорация Майкрософт (Microsoft Corporation), 2008. Руководство по переходу с MySQL на SQL Server 2005 56 CASCADED проверки выполняются для базовых представлений, которые также требуется оценить. Если ключевые слова не указаны, по умолчанию используется режим CASCADED. Пример кода MySQL create table t1 (a int); create view v1 as select * from t1 where a < 2 with check option; create view v2 as select * from v1 where a > 0 with local check option; create view v3 as select * from v1 where a > 0 with cascaded check option; insert into v1 values (2); -- CHECK OPTION failed 'ATest.v1' insert into v2 values (2); insert into v3 values (2); -- CHECK OPTION failed 'ATest.v3' select * from t1; -- 2 Решение Решения пока нет. Проблема: безымянные столбцы в списке выбора представления MySQL автоматически создает имена для безымянных столбцов в списке выбора представления. Пример кода MySQL create table table_name (a int not null, b int not null); insert table_name values (1,2); create view view_name as select a, b, a+b, a*b, now() from table_name; select * from view_name where `a+b`=3; Решение Создайте имена столбцов на базе списка выбора MySQL. Пример кода SQL Server create table table_name (a int not null, b int not null); insert table_name values (1,2); create view view_name as © Корпорация Майкрософт (Microsoft Corporation), 2008. Руководство по переходу с MySQL на SQL Server 2005 57 select a, b, a+b as [a+b], a*b as [a*b], getdate() as [now()] from table_name; select * from view_name where [a+b]=3; Инструкции CREATE EVENT, ALTER EVENT, DROP EVENT Проблема: события MySQL События MySQL — это задачи, которые запускаются в соответствии с расписанием. При создании события вы создаете именованный объект базы данных, содержащий одну или несколько инструкций SQL, которые должны выполняться через один или несколько постоянных интервалов, начинаясь и завершаясь в заданные дату и время. Решение Решения пока нет. Инструкции CREATE, ALTER, DROP PROCEDURE/FUNCTION Проблема: префикс имени базы данных в имени процедуры или функции В отличие от MySQL в SQL Server CREATE/ALTER/DROP PROCEDURE/FUNCTION не позволяет указывать имя базы данных в качестве префикса имени объекта. Пример кода MySQL create function sakila.func_drop () returns float begin declare s float; set s:=3.14; return s; end drop function sakila.func_drop; Решение Решения пока нет. © Корпорация Майкрософт (Microsoft Corporation), 2008. Руководство по переходу с MySQL на SQL Server 2005 58 Проблема: характеристика SQL SECURITY В MySQL характеристику SQL SECURITY можно использовать, чтобы указать, как следует выполнять процедуру: с правами пользователя, создавшего процедуру, или с правами пользователя, вызвавшего ее. Значение по умолчанию — DEFINER. Пример кода MySQL root user: create table table_access (i int not null); insert table_access values (1),(2),(3),(4),(5); create procedure proc_access () sql security definer begin select * from table_access; end grant execute on ATest.* to abc; abc user: select * from ATest.table_access; -- SELECT command denied to user 'abc' call ATest.proc_access(); -- 1 2 3 4 5 root user: drop procedure proc_access; create procedure proc_access () sql security invoker begin select * from table_access; end abc user: select * from ATest.table_access; -- SELECT command denied to user 'abc call ATest.proc_access(); -- SELECT command denied to user 'abc' © Корпорация Майкрософт (Microsoft Corporation), 2008. Руководство по переходу с MySQL на SQL Server 2005 59 Решение Решения пока нет. Проблема: имена параметров процедур В отличие от SQL Server в MySQL не требуется префикс @ для имен переменных процедур. Пример кода MySQL create function func_pi (p int) returns float begin declare s float; set s:=p*pi(); return s; end Решение Преобразуйте имена параметров в процедурах в имена параметров процедур SQL или функций и используйте знак @ в качестве первого знака. Пример кода SQL Server create function func_pi (@p int) returns float begin declare @s float; set @s=@p*pi(); return @s; end Проблема: параметры процедуры INOUT MySQL поддерживает параметры процедуры INOUT. Пример кода MySQL create procedure proc_inout (a int, inout b int) begin set b=b+a; end set @b=0; © Корпорация Майкрософт (Microsoft Corporation), 2008. Руководство по переходу с MySQL на SQL Server 2005 60 call proc_out(7,@b); select @b; -- 7 call proc_out(7,@b); select @b; -- 14 Решение Преобразуйте параметры процедуры MySQL INOUT в параметры процедуры SQL Server OUT. Пример кода SQL Server create procedure proc_inout (@a int, @b int out) as begin set @b=@b+@a; end declare @b int set @b=0; exec proc_inout 7, @b out select @b; -- 7 exec proc_inout 7, @b out select @b; -- 14 Проблема: параметры процедуры OUT MySQL поддерживает параметры процедуры OUT. Параметр OUT передает значение из процедуры объекту, вызвавшему ее. Его начальное значение равно NULL в процедуре, и его значение доступно для вызывающего объекта при возврате процедуры. Пример кода MySQL create procedure proc_out (a int, out b int) begin set b=isnull(b)+a; end set @b=99; call proc_out(7,@b); select @b; -- 8 © Корпорация Майкрософт (Microsoft Corporation), 2008. Руководство по переходу с MySQL на SQL Server 2005 61 call proc_out(7,@b); select @b; -- 8 Решение Решения пока нет. Проблема: ключевое слово AS перед текстом процедуры В MySQL не используется ключевое слово AS перед текстом процедуры. Пример кода MySQL create procedure proc_as (a int, b int) begin select a+b; end Решение Добавьте ключевое слово AS перед текстом процедуры. Пример кода SQL Server create procedure proc_as (@a int, @b int) as begin select @a+@b; end © Корпорация Майкрософт (Microsoft Corporation), 2008. Руководство по переходу с MySQL на SQL Server 2005 62 Инструкции обработки данных В этом разделе поясняются различия между языками обработки данных в MySQL и SQL Server 2005, а также приводятся решения для устранения проблем. Также рассматриваются инструкции SELECT, INSERT, UPDATE и DELETE, преобразования ряда инструкций MySQL, таких как LIMIT, и поясняются различия в синтаксисе объединения. Предложение LIMIT Проблема: предложение LIMIT в инструкциях SELECT С помощью предложения LIMIT можно ограничить результат инструкции MySQL SELECT. Если в предложении LIMIT имеются два аргумента, то первый аргумент задает смещение первой возвращаемой строки, а второй — максимальное число возвращаемых строк. Если в предложении LIMIT имеется один аргумент, его значение задает число возвращаемых строк, считая от начала результирующего набора. Пример кода MySQL select id, data from t1 order by id limit 3, 2; Решение Эмулируйте предложение LIMIT с одним аргументом при помощи предложения TOP инструкции SELECT. Для эмуляции предложения LIMIT с двумя аргументами можно использовать подчиненный запрос с функцией ROW_NUMBER(). Пример кода SQL Server select id, data from ( select id, data, row_number () over (order by id) - 1 as rn from t1 ) rn_subquery where rn between 3 and (3+2)-1 order by id Проблема: предложения LIMIT и ORDER BY в инструкции DELETE с одной таблицей Предложение LIMIT устанавливает предельное число строк, которые можно удалить. Если инструкция DELETE содержит предложение ORDER BY, то строки удаляются в порядке, указанном в этом предложении. Это полезно только при использовании вместе с LIMIT. Пример кода MySQL delete from t3 where data<ascii(id) limit 2; © Корпорация Майкрософт (Microsoft Corporation), 2008. Руководство по переходу с MySQL на SQL Server 2005 63 Решение Эмулируйте предложение LIMIT при помощи предложения TOP инструкции DELETE. Проверьте логику инструкций DELETE с предложениями LIMIT и ORDER BY. Пример кода SQL Server delete top (2) from t3 where data<ascii(id); Проблема: предложения LIMIT и ORDER BY в инструкции UPDATE с одной таблицей Можно использовать предложение LIMIT число_строк для ограничения области инструкции UPDATE. Если инструкция UPDATE содержит предложение ORDER BY, то строки обновляются в порядке, указанном в этом предложении. Пример кода MySQL create table t_upd (id int not null primary key, v varchar(8) not null); insert t_upd values (1,'A'),(2,'B'),(3,'C'); update t_upd set id=id+1; -- Error Code : 1062 Duplicate entry '2' for key 1 update t_upd set id=id+1 order by id desc; -- 3 row(s)affected update t_upd set v=concat(v,'+',v) limit 1; -- 1 row(s)affected Решение Эмулируйте предложение LIMIT при помощи предложения TOP инструкции UPDATE. Проверьте логику инструкций UPDATE с предложениями LIMIT и ORDER BY. Пример кода SQL Server create table t_upd (id int not null primary key, v varchar(8) not null) insert t_upd values (1,'A') insert t_upd values (2,'B') insert t_upd values (3,'C') update t_upd set id=id+1 -- 3 row(s) affected update top (1) t_upd set v=v+'+'+v -- 1 row(s) affected Инструкция DELETE Проблема: DELETE для нескольких таблиц В синтаксисе для нескольких таблиц инструкция DELETE удаляет из каждой таблицы строки, удовлетворяющие указанным условиям. © Корпорация Майкрософт (Microsoft Corporation), 2008. Руководство по переходу с MySQL на SQL Server 2005 64 Пример кода MySQL create table del_a (id int not null, v varchar(8) not null); create table del_b (id int not null, v varchar(8) not null); insert del_a values (1,'A'),(2,'B'),(3,'C'); insert del_b values (1,'C'),(2,'B'),(3,'A'); -- deletes row with id=3 from both tables delete a, b from del_a a, del_b b where a.id=b.id and a.v>b.v; -- deletes row with id=3 only from table del_a delete a from del_a a, del_b b where a.id=b.id and a.v>b.v; Решение Инструкцию DELETE для нескольких таблиц можно эмулировать при помощи отдельных инструкций DELETE для каждой таблицы вместе с переменной таблицы (или временной таблицы) для сохранения промежуточных данных. Пример кода SQL Server create table del_a (id int not null, v varchar(8) not null) create table del_b (id int not null, v varchar(8) not null) insert del_a select 1,'A' union select 2,'B' union select 3,'C' insert del_b select 1,'C' union select 2,'B' union select 3,'A' -- deletes row with id=3 from both tables declare @temp table (id int, v varchar(8)) delete a output deleted.id, deleted.v into @temp from del_a a, del_b b where a.id=b.id and a.v>b.v delete b from @temp a, del_b b where a.id=b.id and a.v>b.v -- deletes row with id=3 only from table del_a delete a from del_a a, del_b b where a.id=b.id and a.v>b.v © Корпорация Майкрософт (Microsoft Corporation), 2008. Руководство по переходу с MySQL на SQL Server 2005 65 Инструкция UPDATE Проблема: UPDATE для нескольких таблиц В синтаксисе для нескольких таблиц инструкция UPDATE обновляет в каждой таблице строки, удовлетворяющие указанным условиям. Пример кода MySQL create table upd_a (id int not null, v varchar(32) not null); create table upd_b (id int not null, v varchar(32) not null); insert upd_a values (1,'A'),(2,'B'),(3,'C'); insert upd_b values (1,'C'),(2,'B'),(3,'A'); update upd_b b, upd_a a set a.v=concat('Z',b.v,'+',b.v), b.v=concat('Z',a.v,'+',a.v) where a.id=b.id and a.v>=b.v; Решение Инструкцию UPDATE для нескольких таблиц можно эмулировать при помощи отдельных инструкций UPDATE для каждой таблицы вместе с переменной таблицы (или временной таблицы) для сохранения промежуточных данных. Пример кода SQL Server create table upd_a (id int not null, v varchar(32) not null) create table upd_b (id int not null, v varchar(32) not null) insert upd_a select 1,'A' union select 2,'B' union select 3,'C' insert upd_b select 1,'C' union select 2,'B' union select 3,'A' declare @temp table (id int, v_old varchar(32), v_new varchar(32)) update b set b.v='Z'+a.v+'+'+a.v output deleted.id, deleted.v, inserted.v into @temp from upd_b b, upd_a a where a.id=b.id and a.v>=b.v; update a set a.v='Z'+b.v_new+'+'+b.v_new © Корпорация Майкрософт (Microsoft Corporation), 2008. Руководство по переходу с MySQL на SQL Server 2005 66 from @temp b, upd_a a where a.id=b.id and a.v>=b.v_old; Инструкция INSERT Проблема: инструкции INSERT и синтаксис VALUES В MySQL инструкции INSERT, использующие синтаксис VALUES, могут вставлять несколько строк. Пример кода MySQL create table tab_ins (id int not null, n numeric(19,9) not null); insert tab_ins values (10,101.80),(20,120.90),(30,150.70); Решение Создайте отдельную инструкцию INSERT для каждой строки. Пример кода SQL Serverы create table tab_ins (id int not null, n numeric(19,9) not null) insert tab_ins values (10,101.80) insert tab_ins values (20,120.90) insert tab_ins values (30,150.70) Проблема: синтаксис INSERT...ON DUPLICATE KEY UPDATE Если в MySQL указывается предложение ON DUPLICATE KEY UPDATE, то при вставке строки, которая может привести к появлению повторяющегося значения в индексе UNIQUE или ключе PRIMARY KEY, для прежней строки выполняется операция UPDATE. В предложении UPDATE можно также использовать функцию VALUES(col_name) для обращения к значениям столбца из части INSERT инструкции INSERT...UPDATE. Пример кода MySQL create table ins_upd (a int not null primary key, b int not null, c int not null); insert ins_upd values (1,2,3),(2,3,4),(1,20,30) on duplicate key update c=values(c); select * from ins_upd; -- 1 2 30, 2 3 4 Решение Проверьте наличие добавленных ключей в индексе и выполните инструкцию INSERT для новых ключей, а инструкцию UPDATE — для существующих. Замените функцию VALUES ее значением. © Корпорация Майкрософт (Microsoft Corporation), 2008. Руководство по переходу с MySQL на SQL Server 2005 67 Пример кода SQL Server create table ins_upd (a int not null primary key, b int not null, c int not null) declare c cursor forward_only static read_only for select 1,2,3 union all select 2,3,4 union all select 1,20,30 declare @a int, @b int, @c int open c fetch c into @a, @b, @c while @@fetch_status=0 begin if not exists (select top 1 0 from ins_upd where a=@a) insert ins_upd values (@a, @b, @c) else update ins_upd set c=@c where a=@a fetch c into @a, @b, @c end close c deallocate c select * from ins_upd; -- 1 2 30, 2 3 4 Проблема: поля INSERT и AUTO_INCREMENT В MySQL инструкция INSERT допускает вставку в поля AUTO_INCREMENT. Пример кода MySQL create table table_autoinc (id int not null auto_increment, v varchar(32) null, key (id)); insert into table_autoinc (v) values ('Value_1'); insert into table_autoinc (v) values ('Value_2'); insert into table_autoinc (v) values ('Value_3'); insert into table_autoinc (id, v) values (40,'Value_4'); insert into table_autoinc (id, v) values (50,'Value_5'); insert into table_autoinc (id, v) values (40,'Value_4_2'); © Корпорация Майкрософт (Microsoft Corporation), 2008. Руководство по переходу с MySQL на SQL Server 2005 68 insert into table_autoinc (id, v) values (40,'Value_4_3'); insert into table_autoinc (v) values ('Value_6'); insert into table_autoinc (v) values ('Value_7'); select * from table_autoinc; -- id: 1,2,3,40,50,40,40,51,52 Решение Для эмуляции этой функциональности можно использовать инструкции SET IDENTITY_INSERT. Пример кода SQL Server create table table_autoinc (id int not null identity(1, 1), v varchar(32) null) create index idx_table_autoinc on table_autoinc (id) insert into table_autoinc (v) values ('Value_1'); insert into table_autoinc (v) values ('Value_2'); insert into table_autoinc (v) values ('Value_3'); set identity_insert table_autoinc on insert into table_autoinc (id, v) values (40,'Value_4'); insert into table_autoinc (id, v) values (50,'Value_5'); insert into table_autoinc (id, v) values (40,'Value_4_2'); insert into table_autoinc (id, v) values (40,'Value_4_3'); set identity_insert table_autoinc off insert into table_autoinc (v) values ('Value_6'); insert into table_autoinc (v) values ('Value_7'); select * from table_autoinc; -- id: 1,2,3,40,50,40,40,51,52 Проблема: выражения в синтаксисе INSERT VALUES Выражения в синтаксисе INSERT VALUES могут ссылаться на любой столбец, заданный ранее в списке значений. Пример кода MySQL create table ins_expr (a float not null, b float not null); insert ins_expr values (sin(4),abs(a)); © Корпорация Майкрософт (Microsoft Corporation), 2008. Руководство по переходу с MySQL на SQL Server 2005 69 Решение Замените ссылку на столбец ее значением. Пример кода SQL Server create table ins_expr (a float not null, b float not null); insert ins_expr values (sin(4),abs(sin(4))); Проблема: вставка явных и неявных значений по умолчанию Если список столбцов и список VALUES пусты, инструкция INSERT создает строку, каждому столбцу которой установлено значение по умолчанию: INSERT INTO имя_таблицы () VALUES(); Пример кода MySQL create table ins_def (a int null, b int not null, c int default 1); insert ins_def values (); -- insert ins_def () values (); select * from ins_def; -- NULL 0 1 Решение Решения пока нет. Инструкция REPLACE Проблема: инструкция REPLACE В MySQL инструкция REPLACE работает в точности как инструкция INSERT, с одним исключением: если в старой строке таблицы такое же значение, как в новой строке для индекса PRIMARY KEY или UNIQUE, то старая строка удаляется перед вставкой новой строки. Пример кода MySQL create table tab_repl (a int not null primary key, b int not null, c int not null); replace tab_repl values (1,2,3),(2,3,4),(1,20,30); select * from tab_repl; -- 1 20 30, 2 3 4 Решение Проверьте наличие добавленных ключей в индексе и выполните инструкцию INSERT для новых ключей, а инструкцию UPDATE — для существующих или удалите существующие ключи перед вставкой. © Корпорация Майкрософт (Microsoft Corporation), 2008. Руководство по переходу с MySQL на SQL Server 2005 70 Пример кода SQL Server create table tab_repl (a int not null primary key, b int not null, c int not null) declare c cursor forward_only static read_only for select 1,2,3 union all select 2,3,4 union all select 1,20,30 declare @a int, @b int, @c int open c fetch c into @a, @b, @c while @@fetch_status=0 begin delete from tab_repl where a=@a; insert tab_repl values (@a, @b, @c) fetch c into @a, @b, @c end close c deallocate c select * from tab_repl; -- 1 20 30, 2 3 4 Инструкция SELECT Проблема: ключевое слово DISTINCTROW В MySQL поддерживается ключевое слово DISTINCTROW в инструкциях SELECT. Решение Замените ключевое слово MySQL DISTINCTROW на ключевое слово SQL Server DISTINCT. Проблема: ссылки в предложении ORDER BY Предложение ORDER BY может ссылаться на поля, отсутствующие в списке SELECT, при использовании инструкции DISTINCT. Пример кода MySQL create table tab_dist (a int, b int); insert tab_dist values (1,30),(2,20),(3,40),(4,20),(5,10); select distinct b from tab_dist order by a desc; -- 10 40 20 30 © Корпорация Майкрософт (Microsoft Corporation), 2008. Руководство по переходу с MySQL на SQL Server 2005 71 Решение Это поведение можно эмулировать при помощи предложения GROUP BY. Пример кода SQL Server create table tab_dist (a int, b int); insert tab_dist values (1,30) insert tab_dist values (2,20) insert tab_dist values (3,40) insert tab_dist values (4,20) insert tab_dist values (5,10) select b from tab_dist group by b order by min(a) desc; Проблема: таблица DUAL В MySQL можно указать DUAL в качестве имени пустой таблицы в случаях, когда нет ссылок на таблицы. Пример кода MySQL select curdate(); select curdate() from dual; select count(*) from dual; -- 1 Решение Обычно можно не обращать внимания на предложения FROM DUAL. Однако инструкцию SELECT COUNT(*) FROM DUAL нужно преобразовать в SELECT 1. Проблема: синтаксис SELECT...FROM...PROCEDURE В MySQL можно определить процедуру Visual C++®, которая может получить доступ и изменить данные запроса перед его отправкой клиенту. Решение Решения пока нет. Инструкции SELECT…INTO и LOAD DATA INFILE Проблема: инструкция SELECT…INTO переменная В MySQL поддерживаются инструкции SELECT…INTO переменная. © Корпорация Майкрософт (Microsoft Corporation), 2008. Руководство по переходу с MySQL на SQL Server 2005 72 Пример кода MySQL select max(host), max(user) into @h, @u from mysql.user; Решение Определите назначение переменной в списке полей. Пример кода SQL Server select @h=max(host), @u=max([user]) from mysql.dbo.[user]; Проблема: инструкции SELECT…INTO и LOAD DATA INFILE В MySQL инструкции SELECT...INTO OUTFILE служат для записи данных из таблицы в файл, а LOAD DATA INFILE — для чтения данных из файла в таблицу. Пример кода MySQL select * into outfile "d:\\in.out\\table_gh.dat" from gh; delete from gh; load data infile "d:/in.out/table_gh.dat" into table gh; Решение Программа SQL Server bcp позволяет записывать таблицы в файлы и загружать содержимое файлов в таблицы. Пример кода SQL Server exec xp_cmdshell 'bcp "select * from ATest.dbo.gh" queryout "d:\table_gh.dat" -c -T' delete from gh exec xp_cmdshell 'bcp ATest.dbo.gh in "d:\table_gh.dat" -k -E -c -T' Предложения GROUP BY, HAVING и ORDER BY Проблема: ссылки на столбцы в предложениях GROUP BY и HAVING В MySQL на столбцы из списка SELECT могут быть ссылки в предложениях GROUP BY или HAVING с использованием псевдонимов столбцов или позиций (только в GROUP BY). Пример кода MySQL create table tab_alias (field_a int, field_b int); insert tab_alias values (1,1),(1,2),(1,3),(2,1),(2,2); select field_a as a, count(*) from tab_alias group by a order by a desc; -- 2 2, 1 3 © Корпорация Майкрософт (Microsoft Corporation), 2008. Руководство по переходу с MySQL на SQL Server 2005 73 select field_b as b, count(*) from tab_alias group by 1 order by 1 desc; -- 3 1, 2 2, 1 2 Решение Ссылки на псевдонимы и позиции столбцов в предложениях GROUP BY и HAVING следует заменить на поля, на которые указывают ссылки. Пример кода SQL Server create table tab_alias (field_a int, field_b int) insert tab_alias select 1,1 union all select 1,2 union all select 1,3 union all select 2,1 union all select 2,2 select field_a as a, count(*) from tab_alias group by field_a order by a desc select field_b as b, count(*) from tab_alias group by field_b order by 1 desc Проблема: сортировка GROUP BY Если в MySQL используется сортировка GROUP BY, возвращаемые строки сортируются в соответствии со столбцами GROUP BY, как если бы для этих столбцов использовалась функция ORDER BY. В MySQL расширены возможности предложения GROUP BY, так что теперь в этом предложении можно указывать также ASC и DESC после поименованных столбцов. Пример кода MySQL select help_category_id, count(*) from mysql.help_topic group by help_category_id desc Решение В SQL Server добавьте предложение ORDER BY для сортировки. Проблема: синтаксис ORDER BY NULL Чтобы избежать ненужной сортировки GROUP BY в MySQL, используется предложение ORDER BY NULL. Пример кода MySQL select host, count(*) from mysql.user group by host order by null Решение Этот синтаксис можно пропустить. © Корпорация Майкрософт (Microsoft Corporation), 2008. Руководство по переходу с MySQL на SQL Server 2005 74 Проблема: в предложениях SELECT и ORDER BY могут быть поля без агрегирования, которые отсутствуют в GROUP BY В MySQL расширены возможности GROUP BY — можно выбирать поля, не упомянутые в предложении GROUP BY. Подобное расширение MySQL применяется к предложению HAVING. В стандартной версии SQL не разрешается указывать в предложении HAVING какой-либо столбец, не найденный в предложении GROUP BY, если он не включен в агрегатную функцию. В MySQL можно использовать эти столбцы для упрощения вычислений. Это расширение предполагает, что несгруппированные столбцы имеют те же указанные для группы значения. Иначе результат будет неопределенным. Пример кода MySQL create table customer (custid int, name varchar(32)); insert customer values (1,'Customer_A'); insert customer values (2,'Customer_B'); insert customer values (3,'Customer_C'); create table orde (custid int, payments numeric(19,2)); insert orde values (1,50.80); insert orde values (1,140.84); insert orde values (2,32.80); select orde.custid, customer.name, max(payments) from orde, customer where orde.custid = customer.custid group by orde.custid; Решение В SQL Server не поддерживаются запросы, поля которых представлены в списке SELECT или предложении ORDER BY без агрегации, но отсутствуют в предложении GROUP BY. Измените запрос, включив эти поля в предложение GROUP BY. Проблема: предложение HAVING без предложения GROUP BY В MySQL можно использовать предложение HAVING без предложения GROUP BY. Пример кода MySQL create table tab_hav (class varchar(128), amount int, date datetime); insert tab_hav values ('PRINTER',2,'20061215'); insert tab_hav values ('SCANNER',3,'20070123'); insert tab_hav values ('FAX',5,'20070918'); © Корпорация Майкрософт (Microsoft Corporation), 2008. Руководство по переходу с MySQL на SQL Server 2005 75 insert tab_hav values ('PRINTER',1,'20070921'); insert tab_hav values ('PHONE',4,'20070308'); insert tab_hav values ('SCANNER',2,'20070514'); insert tab_hav values ('PRINTER',3,'20071011'); select * from tab_hav having sum(amount)=20; -- 1 row select * from tab_hav having max(amount)=20; -- 0 row select * from tab_hav having max(amount)=5; -- 1 row Решение Для эмулирования этой функциональности преобразуйте предложение HAVING в предложение WHERE и используйте подчиненный запрос для вычисления совокупных функций таблицы. Соединения JOIN Проблема: синтаксис JOIN...USING В предложении USING (список_столбцов) указывается список столбцов, которые должны присутствовать в обеих таблицах. Если в обеих таблицах a и b содержатся столбцы c1, c2 и c3, то соответствующие столбцы из двух таблиц будут сравниваться при следующей операции объединения: a LEFT JOIN b USING (c1,c2,c3). Пример кода MySQL create table tab_value (key_a char(8), key_b char(8), key_c char(8), value int); create table tab_subvalue (key_a char(8), key_b char(8), key_c char(8), subvalue int); insert tab_value values ('A','A','A',1),('B','D','E',2),('X','Y','Z',3); insert tab_subvalue values ('A','A','A',100),('A','A','A',120),('B','D','M',200), ('X','Y','Z',318),('X','Y','Z',350); select value, subvalue from tab_value v join tab_subvalue sv using (key_a,key_b,key_c) order by value, subvalue; select value, subvalue from tab_value v join tab_subvalue sv using (key_a,key_b) order by value, subvalue; © Корпорация Майкрософт (Microsoft Corporation), 2008. Руководство по переходу с MySQL на SQL Server 2005 76 Решение Вместо предложения USING используйте предложение ON и задайте условие по всем объединяемым полям. Пример кода SQL Server select value, subvalue from tab_value v join tab_subvalue sv on v.key_a=sv.key_a and v.key_b=sv.key_b and v.key_c=sv.key_c order by value, subvalue select value, subvalue from tab_value v join tab_subvalue sv on v.key_a=sv.key_a and v.key_b=sv.key_b order by value, subvalue Проблема: CROSS JOIN и INNER JOIN В MySQL с точки зрения синтаксиса CROSS JOIN эквивалентен INNER JOIN (они взаимозаменяемы). Пример кода MySQL select value, subvalue from tab_value v inner join tab_subvalue sv order by value, subvalue; select value, subvalue from tab_value v cross join tab_subvalue sv on v.key_a=sv.key_a order by value, subvalue; Решение В MySQL INNER JOIN можно использовать без условий объединения (ON …). В этом случае он работает как CROSS JOIN. В MySQL CROSS JOIN можно использовать с условиями объединения (ON …). В этом случае он работает как INNER JOIN. © Корпорация Майкрософт (Microsoft Corporation), 2008. Руководство по переходу с MySQL на SQL Server 2005 77 Пример кода SQL Server select value, subvalue from tab_value v cross join tab_subvalue sv order by value, subvalue select value, subvalue from tab_value v inner join tab_subvalue sv on v.key_a=sv.key_a order by value, subvalue Проблема: STRAIGHT_JOIN Выражение STRAIGHT_JOIN идентично выражению JOIN, за исключением того, что левая таблица всегда читается раньше правой. Его можно использовать для тех (немногих) случаев, когда оптимизатор объединения располагает таблицы в неправильном порядке. Пример кода MySQL select value, subvalue from tab_value v straight_join tab_subvalue sv order by value, subvalue; select value, subvalue from tab_value v straight_join tab_subvalue sv on v.key_a=sv.key_a order by value, subvalue; Решение Ключевое слово MySQL STRAINT_JOIN решает проблему оптимизации, и в большинстве случаев его можно заменить функцией объединения INNER или CROSS. Пример кода SQL Server select value, subvalue from tab_value v cross join tab_subvalue sv order by value, subvalue select value, subvalue from tab_value v inner join tab_subvalue sv on v.key_a=sv.key_a order by value, subvalue © Корпорация Майкрософт (Microsoft Corporation), 2008. Руководство по переходу с MySQL на SQL Server 2005 78 Проблема: NATURAL JOIN Выражение NATURAL [LEFT] JOIN для двух таблиц определяется как семантический эквивалент выражения INNER JOIN или LEFT JOIN с предложением USING, в котором указываются все столбцы, имеющиеся в обеих таблицах. Пример кода MySQL select value, subvalue from tab_value v natural join tab_subvalue sv order by value, subvalue; Решение Если в объединяемых таблицах имеются столбцы с одинаковыми именами, преобразуйте объединение NATURAL в объединение INNER по этим столбцам. Или же преобразуйте объединение NATURAL в объединение CROSS. Пример кода SQL Server select value, subvalue from tab_value v join tab_subvalue sv on v.key_a=sv.key_a and v.key_b=sv.key_b and v.key_c=sv.key_c order by value, subvalue Subqueries Проблема: подчиненные запросы строк В MySQL поддерживаются подчиненные запросы строк. Подчиненный запрос строки — это вариант подчиненного запроса, который возвращает одну строку и, таким образом, может вернуть несколько значений столбцов. Пример кода MySQL A: select * from gh where (id, value) = row(1, 'A'); B: select * from gh where (id, value) = (select subid, value from gj where gj.id=gh.id); Решение Перепишите инструкции MySQL с подчиненными запросами строк, используя логический оператор AND и условие EXISTS. © Корпорация Майкрософт (Microsoft Corporation), 2008. Руководство по переходу с MySQL на SQL Server 2005 79 Пример кода SQL Server A: select * from gh where id = 1 and value = 'A' B: select * from gh where exists (select 1 from gj where gj.id=gh.id and gj.subid=gh.id and gj.value=gh.value) Подготовленные инструкции Проблема: серверные подготовленные инструкции В MySQL 5.1 поддерживаются серверные подготовленные инструкции. Областью подготовленной инструкции является клиентский сеанс, в котором она создается. PREPARE stmt_name FROM preparable_stmt EXECUTE stmt_name [USING @var_name [, @var_name] ...] {DEALLOCATE | DROP} PREPARE stmt_name Пример create procedure ProcPrepare () begin execute prep_stmt using @a, @b; end prepare prep_stmt from 'select sqrt(pow(?,2) + pow(?,2)) as hypotenuse'; set @a = 3, @b = 4; call procprepare(); deallocate prepare prep_stmt; Решение Решения пока нет. Команда DO Проблема: синтаксис DO В MySQL команда DO выполняет выражения, но не возвращает никаких результатов. Во многих отношениях DO представляет собой краткий вариант предложения SELECT выражение, ..., но работает немного быстрее, когда не нужно возвращать результаты. © Корпорация Майкрософт (Microsoft Corporation), 2008. Руководство по переходу с MySQL на SQL Server 2005 80 Пример 1 select @a:=200; -- sets @a and returns 200 select @a; -- returns 200 do @a:=300; -- sets @a select @a; -- returns 300 Пример 2 create table TableDO (d int not null); create function func_do (par_d int) returns int begin delete from TableDO where d=par_d; return row_count(); end insert TableDO values (1), (2), (3); do func_do(2); select d from TableDO; -- 1, 3 do @r:=func_do(1); select @r, d from TableDO; -- 1 3 Решение Решения пока нет. Обработчики Проблема: интерфейс HANDLER MySQL поддерживает интерфейс HANDLER для чтения данных таблицы. HANDLER tbl_name OPEN [ AS alias ] HANDLER tbl_name READ index_name { = | >= | <= | < } (value1,value2,...) [ WHERE where_condition ] [LIMIT ... ] HANDLER tbl_name READ index_name { FIRST | NEXT | PREV | LAST } [ WHERE where_condition ] [LIMIT ... ] HANDLER tbl_name READ { FIRST | NEXT } [ WHERE where_condition ] [LIMIT ... ] HANDLER tbl_name CLOSE © Корпорация Майкрософт (Microsoft Corporation), 2008. Руководство по переходу с MySQL на SQL Server 2005 81 Инструкция HANDLER предоставляет прямой доступ к интерфейсам системы хранения таблиц. Он доступен для таблиц MyISAM и InnoDB. Пример HANDLER TableA OPEN; HANDLER TableA READ FIRST LIMIT 100; HANDLER TableA CLOSE; Решение Решения пока нет. Модификаторы Проблема: модификаторы (LOW_PRIORITY, DELAYED, HIGH_PRIORITY, QUICK, IGNORE) в инструкциях DML Эти модификаторы MySQL позволяют применять нечто вроде уровня изоляции отдельно для каждой инструкции и управлять выдачей сообщений об ошибках. Решение Решения пока нет. Инструкции транзакций и блокировки В этом разделе описываются основные различия между инструкциями транзакций и управления блокировкой в MySQL и SQL Server 2005, запуск, сохранение и откат, блокировка таблиц, работа с уровнями изоляции и режим AUTOCOMMIT. Инструкции BEGIN TRANSACTION Проблема: другой синтаксис начала транзакции В MySQL и SQL Server используется разный синтаксис для начала транзакции. Решение Вместо инструкций MySQL START TRANSACTION в пакетах и подпрограммах и инструкций BEGIN/BEGIN WORK в пакетах используйте инструкцию SQL Server BEGIN TRANSACTION. Проблема: инструкции начала транзакций неявно сохраняют текущую транзакцию Инструкция начала транзакции MySQL неявно сохраняет текущую транзакцию. Решение Добавьте инструкцию сохранения транзакции с проверкой состояния @@TRANCOUNT перед инструкцией начала транзакции. © Корпорация Майкрософт (Microsoft Corporation), 2008. Руководство по переходу с MySQL на SQL Server 2005 82 Проблема: инструкции, вызывающие неявное сохранение Каждая из следующих инструкций MySQL (и их синонимов) неявно завершает транзакцию, как при выполнении COMMIT перед выполнением инструкции: ALTER FUNCTION, ALTER PROCEDURE, ALTER TABLE, BEGIN, CREATE DATABASE, CREATE FUNCTION, CREATE INDEX, CREATE PROCEDURE, CREATE TABLE, DROP DATABASE, DROP FUNCTION, DROP INDEX, DROP PROCEDURE, DROP TABLE, LOAD MASTER DATA, LOCK TABLES, LOAD DATA INFILE, RENAME TABLE, SET AUTOCOMMIT=1, START TRANSACTION, TRUNCATE TABLE, UNLOCK TABLES. Решение Добавьте инструкцию сохранения транзакции с проверкой состояния @@TRANCOUNT перед инструкциями, вызывающими неявное сохранение. Проблема: инструкции, откат которых невозможен Для некоторых инструкций MySQL невозможно выполнить откат. Как правило, это инструкции языка описания данных (DDL), например те, которые создают или удаляют базы данных, а также те, которые создают, удаляют или изменяют таблицы или хранимые подпрограммы. Решение Решения пока нет. Проблема: системы хранения не поддерживают транзакции Транзакции не влияют на операции с таблицами, которые основаны на системах хранения, не поддерживающих транзакции. Пример кода MySQL create table tran_x (i int not null) engine = innodb; create table tran_y (i int not null) engine = myisam; start transaction; insert tran_x values (7); insert tran_y values (7); rollback; select * from tran_x; -select * from tran_y; -- 7 Решение Решения пока нет. © Корпорация Майкрософт (Microsoft Corporation), 2008. Руководство по переходу с MySQL на SQL Server 2005 83 Инструкции END TRANSACTION Проблема: инструкция окончания транзакции без инструкции начала транзакции Инструкции окончания транзакции можно выполнять до инструкций начала транзакции. Решение Добавьте условие IF (@@TRANCOUNT>0) перед инструкциями COMMIT / COMMIT WORK / ROLLBACK / ROLLBACK WORK. Проблема: предложение CHAIN Предложение MySQL AND CHAIN запускает новую транзакцию сразу после окончания текущей транзакции. Новая транзакция имеет такой же уровень изоляции, как и только что оконченная. Решение Замените предложение MySQL CHAIN на инструкцию SQL Server BEGIN TRANSACTION после инструкции END TRANSACTION. Проблема: предложение RELEASE Предложение RELEASE отключает на сервере текущее подключение клиента после завершения текущей транзакции. Решение Решения пока нет. Именованные инструкции SAVEPOINT транзакций Проблема: различный синтаксис В MySQL и SQL Server используется разный синтаксис для инструкции SAVEPOINT. Пример кода MySQL create table tran_a (i int not null); begin; insert tran_a values (1); start transaction; insert tran_a values (2); rollback and chain; insert tran_a values (3); savepoint spoint; © Корпорация Майкрософт (Microsoft Corporation), 2008. Руководство по переходу с MySQL на SQL Server 2005 84 insert tran_a values (4); rollback to savepoint spoint; commit; select * from tran_a; -- 1 3 Решение Замените инструкции MySQL SAVEPOINT имя_точки_сохранения на инструкции SQL Server SAVE TRANSACTION имя_точки_сохранения. Замените инструкции MySQL ROLLBACK [WORK] TO SAVEPOINT имя_точки_сохранения на инструкции SQL Server ROLLBACK TRANSACTION имя_точки_сохранения. Пример кода SQL Server create table tran_a (i int not null) begin transaction insert tran_a values (1) if (@@trancount>0) commit begin transaction insert tran_a values (2) if (@@trancount>0) rollback begin transaction insert tran_a values (3) save transaction spoint insert tran_a values (4) rollback transaction spoint if (@@trancount>0) commit select * from tran_a -- 1 3 Проблема: инструкция RELEASE SAVEPOINT Инструкция MySQL RELEASE SAVEPOINT удаляет именованную точку сохранения из набора точек сохранения текущей транзакции. Решение Решения пока нет. © Корпорация Майкрософт (Microsoft Corporation), 2008. Руководство по переходу с MySQL на SQL Server 2005 85 Инструкции SET AUTOCOMMIT Проблема: инструкция SET AUTOCOMMIT В MySQL поддерживается инструкция SET AUTOCOMMIT. Пример кода MySQL create table tran_auto (i int not null); set autocommit = 1; insert tran_auto values (10); insert tran_auto values (20); rollback; set autocommit = 0; insert tran_auto values (30); insert tran_auto values (40); rollback; insert tran_auto values (50); commit; select * from tran_auto; -- 10 20 50 Решение Замените SET AUTOCOMMIT = 1 на SET IMPLICIT_TRANSACTIONS OFF. Замените SET AUTOCOMMIT = 0 на SET IMPLICIT_TRANSACTIONS ON. Пример кода SQL Server create table tran_auto (i int not null) set implicit_transactions off insert tran_auto values (10) insert tran_auto values (20) if (@@trancount>0) rollback set implicit_transactions on insert tran_auto values (30) insert tran_auto values (40) if (@@trancount>0) rollback; insert tran_auto values (50) © Корпорация Майкрософт (Microsoft Corporation), 2008. Руководство по переходу с MySQL на SQL Server 2005 86 if (@@trancount>0) commit select * from tran_auto -- 10 20 50 Инструкции LOCK TABLES и UNLOCK TABLES Проблема: синтаксис LOCK TABLES и UNLOCK TABLES LOCK TABLES блокирует базовые таблицы (но не представления) для текущего потока. UNLOCK TABLES снимает все блокировки, удерживаемые текущим потоком. Если поток получает блокировку READ для таблицы, этот поток (и все остальные потоки) может только читать из таблицы. Если поток получает блокировку WRITE для таблицы, то только удерживающий блокировку поток может записывать в таблицу и читать из нее. Для других потоков запись или чтение в таблице заблокированы до тех пор, пока блокировка не будет снята. Решение Решения пока нет. Инструкция SET TRANSACTION ISOLATION LEVEL Проблема: уровень изоляции транзакций MySQL по умолчанию В MySQL по умолчанию используется уровень изоляции транзакций REPEATABLE READ. В SQL Server по умолчанию используется уровень изоляции транзакций READ COMMITTED. Решение Решения пока нет. Инструкции транзакций XA Проблема: транзакции XA Реализация транзакций MySQL XA основана на документе X/Open CAE Distributed Transaction Processing: The XA Specification. Интерфейс XA к серверу MySQL состоит из инструкций SQL, начинающихся с ключевого слова XA. Решение Решения пока нет. © Корпорация Майкрософт (Microsoft Corporation), 2008. Руководство по переходу с MySQL на SQL Server 2005 87 Инструкции администрирования баз данных В этом разделе описывается преобразование административных инструкций MySQL, включая управление учетными записями и обслуживание таблиц. Инструкции управления учетными записями Проблема: инструкции CREATE USER, DROP USER, GRANT, RENAME USER, REVOKE, SET PASSWORD В MySQL и SQL Server используется разный синтаксис для этих инструкций. Решение Решения пока нет. Инструкции обслуживания таблиц Проблема: инструкции ANALYZE TABLE, BACKUP TABLE, CHECK TABLE, CHECKSUM TABLE, OPTIMIZE TABLE, REPAIR TABLE, RESTORE TABLE В SQL Server нет идентичных инструкций. Решение Решения пока нет. Инструкция SET Проблема: присвоение нескольких переменных в инструкциях SET В MySQL в инструкции SET можно указывать несколько назначений переменных, разделяя их запятыми. В SQL Server инструкция SET может содержать только одно назначение переменной. Пример кода MySQL create procedure proc_set_var () begin declare a, b int; set a=10, b=20; select a+b; end call proc_set_var () -- 30 © Корпорация Майкрософт (Microsoft Corporation), 2008. Руководство по переходу с MySQL на SQL Server 2005 88 Решение Преобразуйте каждое назначение переменной в отдельную инструкцию SET. Пример кода SQL Server create procedure proc_set_var as begin declare @a int, @b int set @a=10 set @b=20 select @a+@b end exec proc_set_var -- 30 Проблема: системные переменные сервера (глобальные переменные) Сервер MySQL поддерживает множество системных переменных, определяющих его конфигурацию. Для каждой системной переменной имеется значение по умолчанию. Системные переменные могут задаваться при запуске сервера с использованием параметров в командной строке или файле параметров. Большинство из них может изменяться динамически во время работы сервера посредством инструкции SET, что позволяет изменять режим работы сервера без остановки и перезапуска. Значения системных переменных можно указывать в выражениях. Значения системных переменных могут задаваться глобально при запуске сервера с использованием параметров в командной строке или файле параметров. Многие системные переменные являются динамическими, и их можно изменять во время работы сервера с помощью инструкции SET. Чтобы изменить системную переменную с помощью инструкции SET, укажите ее как var_name, при желании можно указать перед ней модификатор. Для явного указания на то, что переменная является глобальной, перед ее именем укажите GLOBAL или @@global. Для явного указания на то, что переменная является переменной сеанса, перед ее именем укажите SESSION, @@session. или @@. LOCAL и @@local. являются синонимами SESSION и @@session. Если модификатор не указан, инструкция SET изменяет переменную сеанса. В инструкции SET можно указывать несколько назначений переменных, разделяя их запятыми. При изменении переменной сеанса значение продолжает действовать до тех пор, пока сеанс не завершится или пока вы не измените значение переменной. Другие клиенты изменение не видят. При изменении глобальной системной переменной значение запоминается и используется для новых подключений до тех пор, пока сервер не перезагрузится. (Чтобы сделать назначение глобальной системной переменной постоянным, задайте ее в файле параметров.) © Корпорация Майкрософт (Microsoft Corporation), 2008. Руководство по переходу с MySQL на SQL Server 2005 89 Чтобы задать для переменной SESSION значение GLOBAL или скомпилированное по умолчанию значение MySQL в качестве значения GLOBAL, используйте ключевое слово DEFAULT. См. также: режим SQL Пример create table table_inc (id int not null auto_increment, unique key (id), v varchar(8) null); insert table_inc (v) values ('A'); insert table_inc (v) values ('B'); insert table_inc (v) values ('C'); set session auto_increment_increment = 7; insert table_inc (v) values ('D'); insert table_inc (v) values ('E'); set session auto_increment_increment = default; insert table_inc (v) values ('F'); insert table_inc (v) values ('G'); select id from table_inc; -- 1 2 3 8 15 16 17 Решение Решения пока нет. Инструкция SHOW Проблема: синтаксис SHOW SHOW имеет множество форм, предоставляющих сведения о базах данных, таблицах, столбцах и состоянии сервера. См. также: синтаксис DESCRIBE Решение Решения пока нет. © Корпорация Майкрософт (Microsoft Corporation), 2008. Руководство по переходу с MySQL на SQL Server 2005 90 Проблема: синтаксис DESCRIBE DESCRIBE предоставляет сведения о столбцах в таблице. Это ярлык для SHOW COLUMNS FROM. Эти инструкции также отображают сведения о представлениях. Решение Решения пока нет. Другие административные инструкции Проблема: инструкции CACHE INDEX, LOAD INDEX INTO CACHE, FLUSH, RESET, KILL В SQL Server нет идентичных инструкций. Решение Решения пока нет. Хранимые процедуры и функции (процедуры) В этом разделе описываются различия между языком расширения процедур SQL в MySQL и SQL Server. Описываются создание и вызов хранимых процедур и функций, работа с локальными переменными, курсоры и управление инструкциями потока. Инструкции CALL Проблема: синтаксис процедур вызова В MySQL инструкция CALL используется для вызова процедуры. В MySQL поддерживаются выражения в виде параметров вызова. Пример кода MySQL create procedure proc_case (s varchar(64), out s_low varchar(64), out s_up varchar(64)) begin set s_low:=lower(s), s_up:=upper(s); end call proc_case (date_format(now(),'%D %M %Y'),@low,@up); select @low, @up; -- 23rd october 2007, 23RD OCTOBER 2007 © Корпорация Майкрософт (Microsoft Corporation), 2008. Руководство по переходу с MySQL на SQL Server 2005 91 Решение Преобразуйте инструкции MySQL CALL в инструкции Transact-SQL EXEC. Выражения в параметрах вызовов можно вычислить с временными переменными перед инструкциями. Включите ключевое слово OUTPUT для выходных параметров. Пример кода SQL Server create procedure proc_case (@s varchar(64), @s_low varchar(64) out, @s_up varchar(64) out) as begin select @s_low=lower(@s), @s_up=upper(@s) end declare @s varchar(64), @low varchar(64), @up varchar(64) set @s=convert(varchar(64),getdate(),106) exec proc_case @s, @low output, @up output select @low, @up; -- 23 oct 2007, 23 OCT 2007 Составные инструкции Проблема: пустые составные инструкции Пустые составные инструкции (BEGIN END) допустимы в MySQL, но не в Transact-SQL. Пример кода MySQL create procedure empty_block(i int) begin select sin(i); begin end; select cos(i); end Решение Пропускайте такие инструкции. Проблема: помеченные составные инструкции В MySQL составные инструкции можно помечать. Невозможно использовать метку окончания, если нет метки начала. Если есть обе метки, их имена должны совпадать. © Корпорация Майкрософт (Microsoft Corporation), 2008. Руководство по переходу с MySQL на SQL Server 2005 92 Пример кода MySQL create procedure lab_comp() begin s: begin select 'STEP 1'; -- displayed leave s; select 'STEP 2'; -- ignored end; select 'STEP 3'; -- displayed end Решение Эмулируйте поведение LEAVE в помеченной составной инструкции при помощи инструкции Transact-SQL GOTO. Пример кода SQL Server create procedure lab_comp as begin begin select 'STEP 1'; -- displayed goto s; select 'STEP 2'; -- ignored end; s: select 'STEP 3'; -- displayed end Локальные переменные Проблема: объявление переменных одинакового типа MySQL допускает объявление нескольких переменных одного типа в одной инструкции. Пример кода MySQL declare x, y, z int; Решение Объявляйте тип каждой переменной в SQL Server. © Корпорация Майкрософт (Microsoft Corporation), 2008. Руководство по переходу с MySQL на SQL Server 2005 93 Пример кода SQL Server declare @x int, @y int, @z int Проблема: DECLARE c DEFAULT В MySQL предложение DEFAULT предоставляет значение переменной по умолчанию. Это значение можно указать как выражение. Пример кода MySQL declare x, y int default sqrt(225); declare z int default 123; Решение Выполните инициализацию переменной после ее объявления. Пример кода SQL Server declare @x int, @y int select @x=sqrt(225), @y=sqrt(225) declare @z int select @z=123 Проблема: область значения локальных переменных В MySQL область локальной переменной находится внутри блока BEGIN...END, где она объявлена. На переменную можно ссылаться в блоках, размещенных внутри данного блока, кроме блоков, где объявляется переменная с таким же именем. Пример кода MySQL create procedure var_scope() begin declare a, b int; set a=5, b=7; select a, b; -- 5 7 begin declare a int; set a=9; select a, b; -- 9 7 end; select a, b; -- 5 7 end © Корпорация Майкрософт (Microsoft Corporation), 2008. Руководство по переходу с MySQL на SQL Server 2005 94 Решение Решения пока нет. Проблема: имена переменных SQL могут совпадать с именами столбцов Если инструкция SQL содержит ссылку на столбец и объявляет локальную переменную с таким же именем, MySQL интерпретирует ссылку как имя переменной. Пример кода MySQL create procedure var_field() begin create temporary table if not exists vf (a int, b int); insert vf values (1,1),(1,2),(1,3); select a, b from vf; -- 1 1, 1 2, 1 3 begin declare a int default 7; select a, b from vf; -- 7 1, 7 2, 7 3 end; end Решение При преобразовании интерпретируйте двойные ссылки как имя переменной. Пример кода SQL Server create procedure var_field as begin create table #vf (a int, b int) insert #vf values (1,1) insert #vf values (1,2) insert #vf values (1,3) select a, b from #vf -- 1 1, 1 2, 1 3 begin declare @a int set @a=7 select @a, b from #vf -- 7 1, 7 2, 7 3 end; end © Корпорация Майкрософт (Microsoft Corporation), 2008. Руководство по переходу с MySQL на SQL Server 2005 95 Условия и обработчики Проблема: обработка условий MySQL Для управления условиями в MySQL определяются обработчики. Для задания условий, которым необходима специфическая обработка, используется инструкция DECLARE CONDITION. Обработчики задаются инструкцией DECLARE HANDLER. Каждый обработчик обрабатывает одно или несколько условий. При соблюдении одного из этих условий выполняется инструкция (составная инструкция), указанная в обработчике. Пример create table TableCondition_A (c_a int not null); create table TableCondition_B (c_b int not null); create procedure ProcCondition (in par_value int, inout par_null_error int) begin declare cond_a condition for sqlstate value '23000'; -- Error: 1048 SQLSTATE: 23000 (ER_BAD_NULL_ERROR) -- Message: Column '%s' cannot be null declare continue handler for cond_a begin set par_null_error=par_null_error+1; end; set par_null_error=0; insert TableCondition_A values (par_value); insert TableCondition_B values (par_value); end call ProcCondition (null, @err); select @err; -- 2 call ProcCondition (100, @err); select @err; -- 0 Решение Решения пока нет. © Корпорация Майкрософт (Microsoft Corporation), 2008. Руководство по переходу с MySQL на SQL Server 2005 96 Курсоры Проблема: состояние курсора No Data В MySQL требуется обработчик со значением SQLSTATE 02000 для обнаружения состояния курсора No Data. Пример кода MySQL create table t1 (id char(16), data int); create table t2 (i int); create table t3 (id char(16), data int); insert t1 values ('A',65),('K',75),('Q',81),('S',83),('W',87); insert t2 values (10),(100),(20),(200),(30); create procedure curdemo() begin declare done int default 0; declare a char(16); declare b, c int; declare cur1 cursor for select id, data from t1; declare cur2 cursor for select i from t2; declare continue handler for sqlstate '02000' set done = 1; open cur1; open cur2; repeat fetch cur1 into a, b; fetch cur2 into c; if not done then if b < c then insert into t3 values (a,b); else insert into t3 values (a,c); end if; end if; until done end repeat; © Корпорация Майкрософт (Microsoft Corporation), 2008. Руководство по переходу с MySQL на SQL Server 2005 97 close cur1; close cur2; end call curdemo(); select * from t3; -- A 10, K 75, Q 20, S 83, W 30 Решение Используйте переменную Transact-SQL @@FETCH_STATUS для определения состояния последней инструкции курсора FETCH. Пример кода SQL Server create procedure curdemo as begin declare @done int set @done=0 declare @a char(16) declare @b int, @c int declare cur1 cursor forward_only static read_only for select id, data from t1; declare cur2 cursor forward_only static read_only for select i from t2; open cur1; open cur2; while @done=0 begin fetch cur1 into @a, @b; if @@fetch_status<>0 set @done = 1 fetch cur2 into @c; if @@fetch_status<>0 set @done = 1 if @done<>1 begin if @b < @c begin insert into t3 values (@a,@b); end else begin insert into t3 values (@a,@c); end © Корпорация Майкрософт (Microsoft Corporation), 2008. Руководство по переходу с MySQL на SQL Server 2005 98 end end; close cur1 deallocate cur1 close cur2 deallocate cur2 end Конструкции управления потоком Проблема: инструкция IF В MySQL и SQL Server используется разный синтаксис для инструкции IF. Пример кода MySQL if (1>2) then select 'A'; select 'B'; elseif (2>3) then select 'C'; select 'D'; elseif (3>4) then select 'E'; select 'F'; else select 'G'; select 'H'; end if; Решение Инструкцию MySQL IF можно с легкостью эмулировать в SQL Server. Пример кода SQL Server if (1>2) begin select 'A' select 'B' end else if (2>3) begin select 'C' select 'D' end else if (3>4) begin select 'E' select 'F' end else begin select 'G' select 'H' end Проблема: инструкция CASE В MySQL и SQL Server используется разный синтаксис для инструкции CASE. Пример кода MySQL case int_value when 1 then select 'A'; select 'AA'; when 2 then select 'B'; © Корпорация Майкрософт (Microsoft Corporation), 2008. Руководство по переходу с MySQL на SQL Server 2005 99 when 1 then select 'A1'; select 'A2'; -- ignored when 3 then select 'C'; else select 'NULL'; end case; Решение Инструкции CASE можно эмулировать при помощи инструкций SQL Server IF. Пример кода SQL Server if @int_value=1 begin select 'A' select 'AA' end else if @int_value=2 begin select 'B' end else if @int_value=1 begin select 'A1' select 'A2' end else if @int_value=3 begin select 'C' end else begin select 'NULL' end Проблема: инструкции LOOP и REPEAT В SQL Server нет идентичных инструкций. Пример кода MySQL declare i int; set i=0; m: loop set i:=i+1; if (sin(i) - cos(i) < 0) then leave m; end if; end loop; select i; -- 4 set i=0; repeat set i:=i+1; until (sin(i) - cos(i) < -1) end repeat; select i; -- 5 Решение Инструкции MySQL LOOP и REPEAT можно с легкостью эмулировать при помощи инструкций WHILE в SQL Server. © Корпорация Майкрософт (Microsoft Corporation), 2008. Руководство по переходу с MySQL на SQL Server 2005 100 Пример кода SQL Server declare @i int; set @i=0; while 1=1 begin set @i=@i+1; if (sin(@i) - cos(@i) < 0) break; end; select @i; -- 4 set @i=0; while 1=1 begin set @i=@i+1; if (sin(@i) - cos(@i) < -1) break; end select @i; -- 5 Проблема: инструкции LEAVE и ITERATE В SQL Server нет идентичных инструкций. Пример кода MySQL create procedure proc_goto(s varchar(64), a int, b int) begin m1: loop if (a>b) then leave m1; end if; set s:=concat(substring(s,1,a-1), upper(substring(s,a,1)),substring(s,a+1)); set a:=a+1; if (a>b) then iterate m1; end if; set a:=a+1; end loop; select s; end © Корпорация Майкрософт (Microsoft Corporation), 2008. Руководство по переходу с MySQL на SQL Server 2005 101 call proc_goto ('abcdefghijklmnopqrstuvwxyz',5,10) -- abcdEfGhIjklmnopqrstuvwxyz Решение Для эмуляции этой функции используйте инструкции Transact-SQL BREAK и CONTINUE. Пример кода SQL Server create procedure proc_goto (@s varchar(64), @a int, @b int) as begin while 1=1 begin if (@a>@b) break set @s=substring(@s,1,@a-1)+ upper(substring(@s,@a,1))+substring(@s,@a+1,len(@s)); set @a=@a+1; if (@a>@b) continue set @a=@a+1; end select @s end exec proc_goto 'abcdefghijklmnopqrstuvwxyz',5,10 -- abcdEfGhIjklmnopqrstuvwxyz Процедуры Проблема: инструкции DML в функциях Функции MySQL могут содержать инструкции DML. Это не поддерживается в SQL Server. Пример create table TableFuncA (a int not null); create table TableFuncB (b int not null); create function new_func_a (par_int int) returns int begin delete from TableFuncA where a=par_int; return row_count(); © Корпорация Майкрософт (Microsoft Corporation), 2008. Руководство по переходу с MySQL на SQL Server 2005 102 end insert TableFuncA values (10), (20), (20), (30), (30), (30), (40), (40), (40), (40); insert TableFuncB values (20), (40), (50); select new_func_a(b) from TableFuncB; -- 2 4 0 select * from TableFuncA; -- 10 30 30 30 Решение Решения пока нет. Триггеры В этом разделе описывается преобразование триггеров MySQL в триггеры SQL Server 2005. Проблема: триггеры FOR EACH ROW В MySQL поддерживаются триггеры FOR EACH ROW, которые не поддерживаются в SQL Server. Пример кода MySQL create table t_data ( id int not null primary key, v varchar(128) not null, log_date datetime not null); create table t_log ( id int null, action varchar(6) null, v_old varchar(128) null, v_new varchar(128) null, log_date_old datetime null, log_date_new datetime null); create trigger trg_data_ins after insert on t_data for each row begin declare a varchar(6); if (new.v!='') then set a:='INSERT'; else set a:='EMPTY'; end if; insert t_log (id,action,v_old,v_new,log_date_old,log_date_new) © Корпорация Майкрософт (Microsoft Corporation), 2008. Руководство по переходу с MySQL на SQL Server 2005 103 values (new.id,a,null,new.v,null,new.log_date); end insert t_data values (1,'A',now()),(2,'B',now()),(3,'',now()),(4,'C',now()); Решение Для эмуляции функциональности триггера FOR EACH ROW в SQL Server можно использовать курсор. Пример кода SQL Server create trigger trg_data_ins on t_data after insert as begin declare @id int, @v varchar(128), @log_date datetime declare for_each_row cursor forward_only static read_only for select id, v, log_date from inserted declare @a varchar(6); open for_each_row fetch for_each_row into @id, @v, @log_date while @@fetch_status = 0 begin if (@v!='') set @a='INSERT' else set @a='EMPTY'; insert t_log (id,action,v_old,v_new,log_date_old,log_date_new) values (@id,@a,null,@v,null,@log_date); fetch for_each_row into @id, @v, @log_date end close for_each_row deallocate for_each_row end © Корпорация Майкрософт (Microsoft Corporation), 2008. Руководство по переходу с MySQL на SQL Server 2005 104 Проблема: триггеры BEFORE В MySQL поддерживаются триггеры BEFORE. В триггерах MySQL ключевое слово BEFORE указывает, что триггер вызывается до выполнения инструкции срабатывания триггера. Внутри триггера можно обращаться к столбцам зависимой таблицы (таблицы, связанной с триггером) с помощью псевдонимов OLD и NEW. OLD.имя_столбца обращается к столбцу в имеющейся строке до ее обновления или удаления. NEW.имя_столбца обращается к столбцу новой строки, которую нужно вставить, или имеющейся строки после обновления. Пример кода MySQL create trigger trg_data_upd before update on t_data for each row begin set new.log_date:=now(); if (old.v='') then set new.v:=''; end if; insert t_log (id,action,v_old,v_new,log_date_old,log_date_new) values (old.id,'UPDATE',old.v,new.v,old.log_date,new.log_date); end update t_data set v=concat(v,'+',v); Решение Для эмуляции триггера BEFORE в SQL Server можно использовать триггер INSTEAD OF. Пример кода SQL Server create trigger trg_data_upd on t_data instead of update as begin declare @id_old int, @v_old varchar(128), @log_date_old datetime declare @id_new int, @v_new varchar(128), @log_date_new datetime declare for_each_row cursor forward_only static read_only for select id, v, log_date from deleted open for_each_row © Корпорация Майкрософт (Microsoft Corporation), 2008. Руководство по переходу с MySQL на SQL Server 2005 105 fetch for_each_row into @id_old, @v_old, @log_date_old while @@fetch_status = 0 begin select @id_new=id, @v_new=v, @log_date_new=log_date from inserted where id=@id_old set @log_date_new=getdate(); if (@v_old='') set @v_new=''; insert t_log (id,action,v_old,v_new,log_date_old,log_date_new) values (@id_old,'UPDATE',@v_old,@v_new,@log_date_old,@log_date_new); -- insted of ----------------------------------------update t_data set v=@v_new, log_date=@log_date_new where id=@id_old ------------------------------------------------------ fetch for_each_row into @id_old, @v_old, @log_date_old end close for_each_row deallocate for_each_row end Режим SQL (системная переменная SQL_MODE) Проблема: применение различных режимов SQL Сервер MySQL может работать в различных режимах SQL и по-разному применять эти режимы для разных клиентов. Режимы определяют, какой синтаксис SQL поддерживается в MySQL и какие виды проверок достоверности данных выполняются. Для смены и восстановления режима SQL в MySQL используется системная переменная sql_mode. © Корпорация Майкрософт (Microsoft Corporation), 2008. Руководство по переходу с MySQL на SQL Server 2005 106 Пример SET sql_mode = ''; SELECT NOT 1 BETWEEN -5 AND 5; -- 0 SET sql_mode = 'HIGH_NOT_PRECEDENCE'; SELECT NOT 1 BETWEEN -5 AND 5; -- 1 -- (NOT 1) BETWEEN -5 AND 5 Решение Решения пока нет. Перенос системных функций MySQL В этом разделе описываются сопоставление системных функций MySQL с эквивалентными функциями SQL Server 2005 и решения для преобразования функций MySQL. Эквивалентные функции Следующие системные функции MySQL можно использовать так же, как и в коде SQL Server: ASCII, LEFT, LOWER, LTRIM, REPLACE, REVERSE, RIGHT, RTRIM, SOUNDEX, SPACE, SUBSTRING, UPPER, ABS, ACOS, ASIN, ATAN, ATAN2, CEILING, COS, COT, DEGREES, EXP, FLOOR, LOG, LOG10, PI, POWER, RADIANS, RAND, ROUND, SIGN, SIN, SQRT, TAN, DAY, MONTH, COALESCE, NULLIF, CAST, CONVERT. Неподдерживаемые функции Следующие функции MySQL невозможно эмулировать в SQL Server из-за различий в логическом и физическом устройстве, а также в модели безопасности: BENCHMARK, CHARSET, COERCIBILITY, COLLATION, CRC32, DATE_ADD с INTERVAL, DATE_SUB с INTERVAL, GET_FORMAT, PERIOD_ADD, PERIOD_DIFF, SUBTIME, TIMESTAMP, TIMESTAMPADD, TIMESTAMPDIFF, MATCH, EXTRACTVALUE, UPDATEXML, GET_LOCK, IS_FREE_LOCK, MASTER_POS_WAIT, RELEASE_LOCK. Эмулируемые функции Проблема: функции с переменным числом параметров Следующие функции в MySQL имеют переменное число параметров: GREATEST(значение1, значение2, ...) LEAST(значение1, значение2, ...) INTERVAL(N, N1, N2, N3, ...) CHAR(N, ... [USING имя_набора_символов]) ELT(N, строка1, строка2, строка3, ...) FIELD(строка, строка1, строка2, строка3, ...) MAKE_SET(биты, строка1, строка2, ...) © Корпорация Майкрософт (Microsoft Corporation), 2008. Руководство по переходу с MySQL на SQL Server 2005 107 Решение Функции с переменным числом параметров можно эмулировать при помощи функции Transact-SQL CASE. Также можно использовать тип данных XML для передачи данных в функцию эмуляции, но при этом придется дополнительно преобразовывать данные в формат XML и из него. Проблема: IF(expr1, expr2, expr3) Если expr1 равно TRUE (expr1 <> 0 и expr1 <> NULL), то IF() возвращает expr2, а в противном случае — возвращает expr3. Пример кода MySQL if(@a>@b, @a, @b-@a) Решение Для эмуляции этой функции используйте функцию Transact-SQL CASE. Пример кода SQL Server case when @a > @b then @a else @b - @a end Проблема: BIN(N) Возвращает строковое представление двоичного значения N. Решение Для эмуляции этой функции в Transact-SQL используйте строковые функции и битовые операторы. Проблема: BIT_LENGTH(str) Возвращает длину строки str в битах. Решение Для эмуляции этой функции в Transact-SQL используйте функцию DATALENGTH. Проблема: CONCAT(строка1, строка2, …) и CONCAT_WS(разделитель, строка1, строка2, ...) Возвращает строку, возникающую путем объединения аргументов. Пример кода MySQL CONCAT('A','B','C'), CONCAT_WS('#','A','B','C') Решение Используйте оператор SQL Server плюс (+) для объединения строк. © Корпорация Майкрософт (Microsoft Corporation), 2008. Руководство по переходу с MySQL на SQL Server 2005 108 Пример кода SQL Server 'A'+'B'+'C', 'A'+'#'+'B'+'#'+'C' Проблема: CONV(N, from_base, to_base) Преобразует числа из системы счисления с одним основанием в систему счисления с другим основанием. Решение Для эмуляции этой функции используйте математические функции и битовые операции Transact-SQL. Проблема: EXPORT_SET(bits, on, off [, separator [, number_of_bits]]) Возвращает строку, причем для каждого бита, установленного в битах значения, возвращается строка on, а для каждого сброшенного бита — строка off. Решение Для эмуляции этой функции используйте математические функции и битовые операции Transact-SQL. Проблема: FIND_IN_SET(str, strlist) Возвращает значение в диапазоне от 1 до N, если строка str находится в списке строк strlist, состоящем из N подстрок. Решение Для эмуляции этой функции используйте функцию Transact-SQL CHARINDEX. Проблема: FORMAT(X, D) Преобразует число X в формат вида '#,###,###.##' с округлением до D десятичных знаков и возвращает результат в виде строки. Решение Для эмуляции этой функции используйте функции Transact-SQL ROUND и CONVERT. Проблема: HEX(N_or_S) Если аргумент N_or_S является числом, возвращается строковое представление шестнадцатеричного значения N, где N — очень длинное число (BIGINT). Если аргумент N_or_S является строкой, возвращается шестнадцатеричное строковое представление N_or_S, в котором каждый символ N_or_S представлен в виде двух шестнадцатеричных цифр. UNHEX(S) выполняет операцию, обратную HEX(S). © Корпорация Майкрософт (Microsoft Corporation), 2008. Руководство по переходу с MySQL на SQL Server 2005 109 Решение Для эмуляции функции HEX(N_or_S) используйте строковые функции, функции преобразования и битовые операции Transact-SQL. Проблема: INSERT(str, pos, len, newstr) Возвращает строку str, в которой подстрока, начинающаяся с положения pos и включающая len символов, заменяется строкой newstr. Решение Для эмуляции этой функции используйте функции Transact-SQL REPLACE или SUBSTRING. Проблема: LOAD_FILE(file_name) Читает файл и возвращает его содержимое в виде строки. SQL Server не может читать данные из внешнего файла в переменную. Решение Для эмуляции LOAD_FILE(file_name) используйте инструкции массовой загрузки или расширенную хранимую процедуру. Проблема: NOW() Возвращает текущие дату и время. Пример кода MySQL NOW() Решение Используйте аналогичную функцию Transact-SQL GETDATE. Пример кода SQL Server GETDATE() Проблема: REPEAT(str, count) Возвращает строку, состояющую из строки str, повторенной count раз. Пример кода MySQL REPEAT('A', 10) © Корпорация Майкрософт (Microsoft Corporation), 2008. Руководство по переходу с MySQL на SQL Server 2005 110 Решение Используйте аналогичную функцию Transact-SQL REPLICATE. Пример кода SQL Server REPLICATE('A', 10) Проблема: ISNULL(expr) Если expr равно NULL, ISNULL() возвращает 1, в противном случае возвращает 0. Пример кода MySQL ISNULL(@a) Решение Для эмуляции этой функции используйте функцию Transact-SQL CASE и предложение IS NULL. Пример кода SQL Server CASE WHEN @a IS NULL THEN 1 ELSE 0 END Проблема: STRCMP(expr1, expr2) Сравнивает две строки. Решение Попробуйте использовать операторы сравнения Transact-SQL для эмуляции STRCMP(expr1, expr2). Проблема: CONVERT_TZ(dt, from_tz, to_tz) Преобразует значение даты и времени dt из часового пояса from_tz в часовой пояс to_tz и возвращает результирующее значение. В SQL Server отсутствуют функции для часовых поясов. Решение Функции для работы с часовыми поясами можно эмулировать в SQL Server с помощью функции CLR или расширенных хранимых процедур. Проблема: DATE_FORMAT(date, format) Форматирует значение даты в соответствии со строкой формата. В Transact-SQL подобной функции нет. © Корпорация Майкрософт (Microsoft Corporation), 2008. Руководство по переходу с MySQL на SQL Server 2005 111 Решение Для эмуляции функции DATE_FORMAT(date, format) можно использовать функции даты, функции преобразования и строковые функции Transact-SQL. Проблема: FROM_DAYS(N) Дается номер дня N; возвращается значение DATE. Решение Для эмуляции функции FROM_DAYS(N) используйте функцию Transact-SQL CONVERT. Проблема: MAKEDATE(year, dayofyear) Возвращает значение даты указанного года (year) и дня в году (dayofyear). Решение Для эмуляции этой функции используйте функцию Transact-SQL DATEADD. Проблема: SEC_TO_TIME(seconds) Возвращает аргумент seconds, преобразованный в часы, минуты и секунды. Решение Для эмуляции этой функции используйте арифметические операторы и функции преобразования Transact-SQL. Проблема: TIME_TO_SEC(time) Возвращает аргумент time, преобразованный в секунды. Решение Для эмуляции этой функции используйте арифметические операторы и строковые функции Transact-SQL. Проблема: TO_DAYS(date) Дается дата date; возвращается номер дня (номер дня, начиная с года 0). Решение Для эмуляции этой функции используйте функцию Transact-SQL CONVERT. Проблема: BIT_COUNT(N) Возвращает число битов, установленных в аргументе N. © Корпорация Майкрософт (Microsoft Corporation), 2008. Руководство по переходу с MySQL на SQL Server 2005 112 Решение Для эмуляции функции MySQL BIT_COUNT(N) в Transact-SQL используйте строковые функции и битовые операторы. Проблема: функции шифрования и сжатия AES_ENCRYT. AES_DECRYPT. COMPRESS. UNCOMPRESS. ENCODE. DECODE. DES_ENCRYPT. DES_DECRYPT. ENCRYPT. MD5. OLD_PASSWORD. PASSWORD. SHA. SHA1. UNCOMPRESSED_LENGTH. Решение Для эмуляции этих функций используйте функции безопасности и криптографические функции SQL Server. Проблема: LAST_INSERT_ID() Возвращает первое автоматически созданное значение, которое было задано для столбца AUTO_INCREMENT самой последней инструкцией INSERT или UPDATE для изменения столбца. Решение Для эмуляции LAST_INSERT_ID() используйте функцию Transact-SQL @@IDENTITY или SCOPE_IDENTITY. Проблема: DEFAULT(column) Возвращает значение по умолчанию для столбца таблицы. Решение Для эмуляции DEFAULT(column) используйте данные системного представления. Проблема: INET_ATON(expr) Дается сетевой адрес, представленный четверкой чисел, разделенных точками, в виде строки; возвращается целое число, представляющее числовое значение адреса. INET_NTOA(expr). Дается числовой адрес (4 или 8 байт); возвращается строка, содержащая представление адреса в формате четырех чисел, разделенных запятыми. Решение Для эмуляции этих функций используйте арифметические операторы и строковые функции Transact-SQL. Проблема: GROUP_CONCAT(expr) Эта функция возвращает строковый результат со сцепленными ненулевыми значениями из группы. © Корпорация Майкрософт (Microsoft Corporation), 2008. Руководство по переходу с MySQL на SQL Server 2005 113 Решение Эту функцию можно эмулировать с использованием кода Transact-SQL, показанного в следующем примере: declare @v varchar(max) set @v='' select @v=@v+','+isnull(field_a,'') from table_1 select substring(@v,2,len(@v)) Проблема: INSTR(str, substr), POSITION(substr IN str) Возвращает положение первого вхождения подстроки substr в строку str. LOCATE(substr, str [, pos]). Возвращает положение первого вхождения подстроки substr в строку str, начиная с положения pos. Решение Для эмуляции этой функциональности используйте функцию CHARINDEX. Заключение Из этого руководства по миграции вы узнали о различиях между платформами баз данных MySQL и SQL Server 2005 и действиях, необходимых для преобразования базы данных MySQL в SQL Server. Дополнительные сведения Веб-сайт SQL Server Технический центр SQL Server Центр разработчиков SQL Server Насколько полезным оказался для вас этот документ? Сообщите нам свое мнение. Как бы вы оценили документ по шкале от 1 балла (очень плохой) до 5 (отличный)? Почему вы поставили такую оценку? Например так, как показано на рисунке. Вы поставили высокую оценку, потому что документ содержит отличные примеры и иллюстрации, написан хорошим языком или по иной причине? Вы поставили низкую оценку, потому что документ содержит плохие примеры, неудачные иллюстрации или непонятно написан? Ваш отзыв поможет нам улучшить качество выпускаемых информационных документов. Отправить отзыв. © Корпорация Майкрософт (Microsoft Corporation), 2008.