Guide To Migrating From DB2 To SQL Server and Azure SQL DB
Guide To Migrating From DB2 To SQL Server and Azure SQL DB
Guide To Migrating From DB2 To SQL Server and Azure SQL DB
Azure SQL DB
SQL Server Technical Article
Authors: Alexander Pavlov (DB Best Technologies), Andrey Khudyakov (DB Best Technologies), Oksana
Eremenko (DB Best Technologies), Stanislav Sklyarov (DB Best Technologies), Alexander Vasyuk (DB Best
Technologies)
Technical Reviewers: Dmitry Balin (DB Best Technologies)
Editor: Peter Skjøtt Larsen (DB Best Technologies)
Summary
In this migration guide you will learn the differences between the IBM DB2 and Microsoft SQL
Server database platforms, and the steps necessary to convert a DB2 database to SQL Server and Azure
SQL DB.
Guide to Migrating from DB2 to SQL Server and Azure SQL DB Page | 1
Copyright
This is a preliminary document and may be changed substantially prior to final commercial release
of the software described herein.
The information contained in this document represents the current view of Microsoft Corporation on
the issues discussed as of the date of publication. Because Microsoft must respond to changing
market conditions, it should not be interpreted to be a commitment on the part of Microsoft, and
Microsoft cannot guarantee the accuracy of any information presented after the date of publication.
This White Paper is for informational purposes only. MICROSOFT MAKES NO WARRANTIES,
EXPRESS, IMPLIED OR STATUTORY, AS TO THE INFORMATION IN THIS DOCUMENT.
Complying with all applicable copyright laws is the responsibility of the user. Without limiting the
rights under copyright, no part of this document may be reproduced, stored in or introduced into a
retrieval system, or transmitted in any form or by any means (electronic, mechanical, photocopying,
recording, or otherwise), or for any purpose, without the express written permission of Microsoft
Corporation.
Microsoft may have patents, patent applications, trademarks, copyrights, or other intellectual
property rights covering subject matter in this document. Except as expressly provided in any
written license agreement from Microsoft, the furnishing of this document does not give you any
license to these patents, trademarks, copyrights, or other intellectual property.
Unless otherwise noted, the example companies, organizations, products, domain names, e-mail
addresses, logos, people, places and events depicted herein are fictitious, and no association with
any real company, organization, product, domain name, email address, logo, person, place or
event is intended or should be inferred.
Microsoft. SQL Server, and Visual C++ are registered trademarks of Microsoft Corporation in the
United States and other countries.
The names of actual companies and products mentioned herein may be the trademarks of their
respective owners.
Guide to Migrating from DB2 to SQL Server and Azure SQL DB Page | 2
Contents
Guide to Migrating from DB2 to SQL Server and Azure SQL DB Page | 4
4.2.6 Casting Functions ........................................................................................................................ 164
4.2.7 Aggregation Functions ................................................................................................................. 170
Guide to Migrating from DB2 to SQL Server and Azure SQL DB Page | 5
1.0 Contents Introduction
This migration guide outlines the procedures, issues, and solutions for migrating from IBM DB2 version 10.5
… for Linux, UNIX, or Windows® to Microsoft® SQL Server® 2014 and Azure SQL DB database software. The
solutions provided here can also be applied to DB2 UDB for z/OS versions 9.0 and 10.0.
Guide to Migrating from DB2 to SQL Server and Azure SQL DB Page | 6
2.2 Migrating Security Items
There are three main mechanisms within DB2 that allow you to implement a database security plan:
authentication, authorization, and privileges. This section first covers these security items, which differ from
those used in SQL Server. A separate issue addressed in this section is the conversion of database users.
Guide to Migrating from DB2 to SQL Server and Azure SQL DB Page | 7
2.2.1 Authentication
In DB2 client-server applications, login and password checking can be performed on a server, a client, or an
intermediate DB2 Connect gateway. Five different types of authentication are available for defining the location:
SERVER, SERVER_ENCRYPT, CLIENT, KERBEROS, and KRB_SERVER_ENCRYPT.
Authentication in SQL Server is always performed on the server side only, and is implemented in a different
way than in DB2. Because SQL Server offers two different modes: Windows Authentication and Mixed
Authentication. Depending on the system architecture, authentication should be adjusted during the
conversion.
To manage a trusted connection, DB2 uses the instance configuration parameters TRUST_ALLCLNTS and
TRUST_CLNTAUTH. When a user connects to SQL Server using Windows Authentication (by means of a
Windows login), SQL Server relies on (“trusts”) the operating system to perform authentication checking, and
checks only if the Windows user name corresponds to a login that is defined in this instance of SQL Server or
if the user’s login belongs to a Windows group with a login that is defined in SQL Server. Thus, it is sufficient
to use Windows Authentication mode to implement a trusted connection in SQL Server.
2.2.2 Authorization
User authorization defines the list of the commands and objects that are available for a user. This list thereby
controls user actions.
In DB2, there are predetermined groups of privileges for authorization, both at the instance level and at the
level of a DB2 database.
Instance level: The privileges at the instance level—SYSADM, SYSCTRL, and SYSMAINT—can be
granted only to users and user groups of the operating system.
Database level: DBADM and LOAD privileges are granted only on a particular database. Using the
GRANT command, the DBADM and LOAD privileges can be granted to an existing user group of the
operating system, to a DB2 user group, and to individual users of the operating system or database.
SQL Server has a similar authorization mechanism. Some DB2 privileges of the instance level or the database
level can be replaced in SQL Server with predefined server roles, database roles, or a combination. Some
privileges can also be replaced by creating new roles at the database level, which can be assigned to users
and groups.
2.2.3 Privileges
DB2 privileges generally fall into two main categories: database-level privileges, which span all objects within
the database, and object-level privileges, which are associated with a specific object. Converting privileges to
SQL Server permissions is performed by means of permission sets, which can be granted to both users and
groups. Depending on whether database-level or object-level privileges are being converted, access
permissions to a definite database or its objects are granted to either groups or users in SQL Server. All existing
DB2 privileges can be replaced with the equivalent SQL Server permissions or a combination of permissions.
Guide to Migrating from DB2 to SQL Server and Azure SQL DB Page | 8
2.3 Mapping Data Types
Most data types used in DB2 do not have exact equivalents in Microsoft SQL Server. They differ in scale,
precision, length, and functionality. This specification explains the data type mapping for table columns and
includes remarks about conversion issues.
Section 3.11, “User-Defined Types,” covers migration of user-defined types.
2.3.2 The DB2 Version 10.5.0 for Linux, UNIX, and Windows
Built-in Data Types
Table 2. DB2 LUW Built in Data Types.
Guide to Migrating from DB2 to SQL Server and Azure SQL DB Page | 9
Area DB2 Data Type SQL Data Type
Datetime Date date
Time time
Timestamp timestamp
String Character Fixed Length char
Varying Length varchar
clob
Graphics Fixed Length graphic
Varying Length vargraphic
dbclob
Binary Varying Length blob
Signed Numeric Exact Binary Integer 16 Bit smallint
32 Bit integer
64 Bit bigint
Decimal Packed decimal
Decimal Floating Point decfloat
Approximate Floating Point Single Precision real
Double Precision double
Extensible markup language xml
Guide to Migrating from DB2 to SQL Server and Azure SQL DB Page | 10
Table 3. Character Strings data types - lists the recommended type mappings for converting
table columns.
DB2 v.10 for z/OS DB2 Recommended SQL Server Alternative
and data type SQL Server 2012/2014 SQL Server
DB2 v.10.5.0 description 2012/2014 and 2012/2014
for LUW and Azure SQL DB and
data type Azure SQL DB Data Type Azure SQL DB
Data Type description Data Types
CHARACTER(n) Fixed-length char [(n)] Fixed-length, non- varchar(n)-
character strings Unicode string variable-length non-
with a length data. Unicode string
of n bytes. n must n defines the string data. n 1-8000
be greater than 0 length and must be characters;
and not greater than a value from 1 nchar(n)- fixed-
255. The default through 8,000. The length Unicode
length is 1. storage size string data. n 1-
is n bytes. The ISO 4000 characters;
synonym nvarchar(n) -
for char is character variable-length
. Unicode string
data. n 1-4000
characters;
DB2 CHARACTER(n) data type can be successfully mapped to char [(n)] data type.
VARCHAR(n) Varying-length varchar [(max)] Variable-length, text-variable-length
character strings non-Unicode string non-Unicode data.
with a maximum data. Length 2^31 - 1
length n defines the string (2,147,483,647)
of n bytes. n must length and can be a characters;
be greater than 0 value from 1 nvarchar(n|max)-
and less than a through variable-length
number that 8,000. max indicate Unicode string
depends on the s that the maximum data. n 1-4000
page size of the storage size is characters.
table space. The 2^31-1 bytes (2 max indicates size
maximum length is GB). The storage 2^31-1 bytes (2
32704. size is the actual GB);
length of the data ntext-variable-
entered + 2 bytes. length Unicode
data. Length is 2^30
- 1 (1,073,741,823)
characters;
varchar(n)-
variable-length non-
Unicode string
data. n 1-8000
characters;
char (n) - non-
Unicode string
data. n 1-8000
characters;
DB2 VARCHAR(n) data type can be successfully mapped to SQL Server varchar [(max)] data type.
CLOB(n) Varying-length varchar [(max)] Variable-length, nvarchar(max)-
character strings non-Unicode string variable-length
with a maximum data. Unicode string
Guide to Migrating from DB2 to SQL Server and Azure SQL DB Page | 11
DB2 v.10 for z/OS DB2 Recommended SQL Server Alternative
and data type SQL Server 2012/2014 SQL Server
DB2 v.10.5.0 description 2012/2014 and 2012/2014
for LUW and Azure SQL DB and
data type Azure SQL DB Data Type Azure SQL DB
Data Type description Data Types
of n characters. n ca max indicates that data. max indicates
nnot exceed the maximum size 2^31-1 bytes (2
2 147 483 647. The storage size is GB);
default length is 1M. 2^31-1 bytes (2 text-variable-length
GB). The storage non-Unicode data.
size is the actual Length 2^31 - 1
length of the data (2,147,483,647)
entered + 2 bytes. characters;
ntext-variable-
length Unicode
data. Length is 2^30
- 1 (1,073,741,823)
characters;
The best choice for migrating DB2 large object types (LOBs) such as CLOB(n) is SQL Server
varchar(max) data type.
GRAPHIC(n) Fixed-length graphic nchar [(n)] Fixed-length nvarchar(n)-
strings that Unicode string variable-length
contain n double- data. Unicode string
byte n defines the string data. n 1-4000
characters. n must length and must be characters;
be greater than 0 a value from 1 ntext-variable-
and less than 128. through 4,000. The length Unicode
The default length is storage size is two data. Length is 2^30
1. times n bytes. When - 1 (1,073,741,823)
the collation code characters;
page uses double-
byte characters, the
storage size is
still n bytes.
Depending on the
string, the storage
size of n bytes can
be less than the
value specified
for n.
DB2 GRAPHIC(n) data type can be successfully mapped to SQL Server nchar [(n)] data type.
VARGRAPHIC(n) Varying-length nvarchar [(max)] Variable-length ntext-variable-
graphic strings that Unicode string length Unicode
contain n double- data. data. Length is 2^30
byte. The maximum max indicates that - 1 (1,073,741,823)
length, n, must be the maximum characters;
greater than 0 and storage size is nchar [(n)]-fixed-
less than a number 2^31-1 bytes (2 length Unicode
that depends on the GB). The storage string data. n 1-
page size of the size, in bytes, is two 4000;
table space. The times the actual
maximum length is length of data
16352. entered + 2 bytes.
Guide to Migrating from DB2 to SQL Server and Azure SQL DB Page | 12
DB2 v.10 for z/OS DB2 Recommended SQL Server Alternative
and data type SQL Server 2012/2014 SQL Server
DB2 v.10.5.0 description 2012/2014 and 2012/2014
for LUW and Azure SQL DB and
data type Azure SQL DB Data Type Azure SQL DB
Data Type description Data Types
DB2 VARGRAPHIC(n) data type can be successfully mapped to SQL Server nvarchar [(max)] data type.
* Restriction: BINARY(n) data type can use as table column only in DB2 Version 10 for z/OS.
DB2 Version 10 for z/OS BINARY(n) data type can be successfully mapped to SQL Server binary [(n)]
data type.
VARBINARY(n) * Varying-length varbinary [(max)] Variable-length varbinary [(n)] -
binary strings with a binary data. variable-length
length of n bytes. max indicates that binary data. n can
The length the maximum be a value from 1
of n must be greater storage size is through 8000 bytes;
than 0 and less than 2^31-1 bytes. The binary [(n)]-fixed-
a number that storage size is the length binary data
depends on the actual length of the with a length
page size of the data entered + 2 of n bytes,
table space. The bytes. The data that where n 1-8000
maximum length is is entered can be 0 bytes;
32704. bytes in length.
* Restriction: VARBINARY(n) data type can use as table column only in DB2 Version 10 for z/OS.
DB2 Version 10 for z/OS VARBINARY(n) data type can be successfully mapped to
SQL Server varbinary [(max)] data type.
BLOB(n) Varying-length varbinary [(max)] Variable-length image - binary data,
binary strings with a binary data. size is 0 - 2^31 – 1
length
Guide to Migrating from DB2 to SQL Server and Azure SQL DB Page | 13
DB2 v.10 for z/OS DB2 Recommended SQL Server Alternative
and data type SQL Server 2012/2014 SQL Server
DB2 v.10.5.0 description 2012/2014 and 2012/2014
for LUW and Azure SQL DB and
data type Azure SQL DB Data Type Azure SQL DB
Data Type description Data Types
of n bytes. n cannot max indicates that (2 147 483 647)
exceed the maximum bytes (2 GB);
2 147 483 647. The storage size is binary- fixed-length
default length is 1M. 2^31-1 bytes. The binary data with a
storage size is the length of n bytes,
actual length of the where n is a value
data entered + 2 from 1 through 8000
bytes. The data that bytes;
is entered can be 0
bytes in length.
The best choice for migrating DB2 large object types (LOBs) as BLOB(n) is SQL Server varbinary(max)
data type.
LONG VARCHAR varchar [(max)] text;
nvarchar(n|max);
ntext;
varchar(n);
char (n);
[LONG] VARCHAR varbinary [(max)] varbinary [(n)];
(n) FOR BIT DATA image;
binary;
LONG nvarchar [(max)] ntext;
VARGRAPHIC nchar [(n)];
CHAR (N) FOR BIT binary [(n)] varbinary [(n)];
DATA
A binary string is a sequence of bytes. Unlike character strings, which usually contain text data, binary
strings are used to hold non-traditional data such as pictures, voice, or mixed media. Character strings of the
FOR BIT DATA subtype may be used for similar purposes, but the two data types are not compatible. The
BLOB scalar function can be used to cast a FOR BIT DATA character string to a binary string.
Certain database columns can be declared FOR BIT DATA. These columns, which generally contain
characters, are used to hold binary information. The CHAR(n), VARCHAR, LONG VARCHAR can contain
binary data. Use these data types when working with columns with the FOR BIT DATA attribute.
A graphic string is a sequence of bytes that represents double-byte character data. The length of the string
is the number of double-byte characters in the sequence. If the length is zero, the value is called the empty
string. This value should not be confused with the null value. Graphic strings are not supported in a database
defined with a single-byte code page.
Graphic strings are not checked to ensure that their values contain only double-byte character code points.
(The exception to this rule is an application precompiled with the WCHARTYPE CONVERT option. In this case,
validation does occur.) Rather, the database manager assumes that double-byte character data is contained
in graphic data fields. The database manager does check that a graphic string value is an even number of
Guide to Migrating from DB2 to SQL Server and Azure SQL DB Page | 14
bytes long. This data type cannot be created in a table. It can only be used to insert data into and retrieve data
from the database.
All values in a fixed-length graphic string column have the same length, which is determined by the length
attribute of the column. The length attribute must be between 1 and 127, inclusive.
Originally VARCHAR was limited to a length of 255, so LONG VARCHAR was needed, but obviously they
both support about 32K now VARCHAR supports 28 bytes less. One big difference between VARCHAR and
LONG VARCHAR is that LONG VARCHAR is stored in a separate area like a LOB (CLOB, BLOB, etc) and
also like a LOB, does not use bufferpools, so every select, insert, update, or delete of a LONG VARCHAR
requires direct disk I/O, just like LOBs.
Table 4. Numeric data types. - lists the recommended type mappings for converting table
columns.
DB2 v.10 for z/OS DB2 Recommended SQL Server Alternative
and data type SQL Server 2012/2014 SQL Server
DB2 v.10.5.0 description 2012/2014 and 2012/2014
for LUW and Azure SQL DB and
data type Azure SQL DB Data Type Azure SQL DB
Data Type description Data Types
SMALLINT Small integers. smallint Range -2^15 (- int-range -2^15 to
A small integer is 32,768) to 2^15-1 2^15-1, storage 4
binary integer with a (32,767) Storage 2 bytes;
precision of 15 bits. Bytes bigint--2^63 to
The range is -32768 2^63-1, storage 8
to +32767. bytes;
Guide to Migrating from DB2 to SQL Server and Azure SQL DB Page | 15
DB2 v.10 for z/OS DB2 Recommended SQL Server Alternative
and data type SQL Server 2012/2014 SQL Server
DB2 v.10.5.0 description 2012/2014 and 2012/2014
for LUW and Azure SQL DB and
data type Azure SQL DB Data Type Azure SQL DB
Data Type description Data Types
tinyint- 0 to 255,
storage 1 byte;
DB2 SMALLINT data type can be successfully mapped to SQL Server smallint data type.
INTEGER or Large integers. int Range -2^31 (- bigint--2^63 to
INT A large integer is 2,147,483,648) to 2^63-1, storage 8
binary integer with a 2^31-1 bytes;
precision of 31 bits. (2,147,483,647) smallint- -2^15 to
The range is - Storage 4 Bytes 2^15-1, storage 2
2147483648 to bytes;
+2147483647. tinyint- 0 to 255,
storage 1 byte;
DB2 INTEGER data type can be successfully mapped to SQL Server int data type.
BIGINT Big integers. A big bigint Range -2^63 (- int-range -2^15 to
integer is a binary 9,223,372,036,854, 2^15-1, storage 4
integer with a 775,808) to 2^63-1 bytes;
precision of 63 bits. (9,223,372,036,854, smallint- -2^15 to
The range of big 775,807) 2^15-1, storage 2
integers is - Storage 8 Bytes bytes;
9223372036854775 tinyint- 0 to 255,
808 to storage 1 byte;
+922337203685477
5807.
DB2 BIGINT data type can be successfully mapped to SQL Server bigint data type.
DECIMAL A decimal number is decimal [(p[ ,s])] Fixed precision and
or a packed decimal and scale numbers.
NUMERIC number with an numeric[(p[ ,s])] When maximum
implicit decimal precision is used,
point. The position valid values are
of the decimal point from - 10^38 +1
is determined by the through 10^38 - 1.
precision and the p (precision) - the
scale of the number. maximum total
The scale, which is number of decimal
the number of digits digits that will be
in the fractional part stored, both to the
of the number, left and to the right
cannot be negative of the decimal point.
or greater than the The precision must
precision. The be a value from 1
maximum precision through the
is 31 digits. maximum precision
All values of a of 38. The default
decimal column precision is 18.
have the same s (scale) - the
precision and scale. number of decimal
The range of a digits that will be
decimal variable or stored to the right of
the numbers in a the decimal point.
Guide to Migrating from DB2 to SQL Server and Azure SQL DB Page | 16
DB2 v.10 for z/OS DB2 Recommended SQL Server Alternative
and data type SQL Server 2012/2014 SQL Server
DB2 v.10.5.0 description 2012/2014 and 2012/2014
for LUW and Azure SQL DB and
data type Azure SQL DB Data Type Azure SQL DB
Data Type description Data Types
decimal column is - This number is
n to +n, where n is substracted
the largest positive from p to determine
number that can be the maximum
represented with the number of digits to
applicable precision the left of the
and scale. The decimal point. The
maximum range is 1 maximum number
- 10³¹ to 10³¹ - 1. of decimal digits
that can be stored
to the right of the
decimal point. Scale
must be a value
from 0 through p.
Scale can be
specified only if
precision is
specified. The
default scale is 0;
therefore, 0
<= s <= p.
Maximum storage
sizes vary, based
on the precision.
DB2 DECIMAL or NUMERIC data types can be successfully mapped to SQL Server decimal [(p[ ,s])]
and numeric[(p[ ,s])] data types respectively.
DECFLOAT A decimal floating- numeric[(p[ ,s])] Fixed precision and
point value is an scale numbers.
IEEE 754r number When maximum
with a decimal point. precision is used,
The position of the valid values are
decimal point is from - 10^38 +1
stored in each through 10^38 - 1
decimal floating-
point value. The
maximum precision
is 34 digits.
The range of a
decimal floating-
point number is
either 16 or 34 digits
of precision; the
exponent range is
respectively 10-383
to 10+384 or 10-
6143 to 10+6144.
DB2 DECFLOAT data type can be successfully mapped to SQL Server numeric[(p[ ,s])] data type.
REAL A single-precision real Range - 3.40E + 38
floating- to -1.18E - 38, 0
Guide to Migrating from DB2 to SQL Server and Azure SQL DB Page | 17
DB2 v.10 for z/OS DB2 Recommended SQL Server Alternative
and data type SQL Server 2012/2014 SQL Server
DB2 v.10.5.0 description 2012/2014 and 2012/2014
for LUW and Azure SQL DB and
data type Azure SQL DB Data Type Azure SQL DB
Data Type description Data Types
point number is a and 1.18E - 38 to
short floating-point 3.40E + 38 storage
number of 32 bits. 4 bytes
The range of single- Approximate-
precision floating- number data types
point numbers is for use with floating
approximately - point numeric data.
7.2E+75 to Floating point data
7.2E+75. In this is approximate;
range, the largest therefore, not all
negative value is values in the data
about -5.4E-79, and type range can be
the smallest positive represented exactly.
value is about 5.4E-
079.
DB2 REAL data type can be successfully mapped to SQL Server real data type.
DOUBLE A double-precision float [(n|53)] Range - 1.79E+308
floating- to -2.23E-308, 0
point number is a and 2.23E-308 to
long floating-point 1.79E+308
number of 64-bits. Approximate-
The range of number data types
double-precision for use with floating
floating-point point numeric data.
numbers is Floating point data
approximately - is approximate;
7.2E+75 to therefore, not all
7.2E+75. In this values in the data
range, the largest type range can be
negative value is represented exactly.
about -5.4E-79, and n is the number of
the smallest positive bits that are used to
value is about 5.4E- store the mantissa
079. of the float number
in scientific notation
and, therefore,
dictates the
precision and
storage size. If n is
specified, it must be
a value
between 1 and 53.
The default value
of n is 53.
DB2 DOUBLE data type can be successfully mapped to SQL Server float [(n|53)] data type.
Guide to Migrating from DB2 to SQL Server and Azure SQL DB Page | 18
The datetime data types are DATE, TIME, and TIMESTAMP.
All SQL Server date, time, and timestamp data types listed in the Table 5 applies to: SQL Server (SQL
Server 2008 through current version), Windows Azure SQL Database (Initial release through current release).
All DB2 date, time, and timestamp data types listed in the Table 5 applies to: DB2 Version 10.5.0 for Linux,
UNIX, and Windows, DB2 Version 10 for z/OS.
DB2 DATE data type can be successfully mapped to SQL Server date data type.
Guide to Migrating from DB2 to SQL Server and Azure SQL DB Page | 20
DB2 v.10 DB2 Recommended SQL Server Alternative
for z/OS data type SQL Server 2012/2014 SQL Server
and description 2012/2014 and 2012/2014
DB2 v.10.5.0 and Azure SQL DB and
for LUW Azure SQL DB Data Type Azure SQL DB
data type Data Type description Data Types
hh is two digits,
ranging from 00 to
23, that represent
the hour.
mm is two digits,
ranging from 00 to
59, that represent
the minute.
ss is two digits,
ranging from 00 to
59, that represent
the second.
n* is zero to seven
digits, ranging from
0 to 9999999, that
represent the
fractional seconds.
hh is two digits that
range from -14 to
+14.
mm is two digits that
range from 00 to 59.
DB2 TIMESTAMP data type can be successfully mapped to SQL Server datetimeoffset data type.
* Restriction: Timestamps can hold timezone information only for DB2 Version 10 for z/OS.
Guide to Migrating from DB2 to SQL Server and Azure SQL DB Page | 22
Table 4. XML, ROWID data types
DB2 v.10 DB2 Recommended SQL Server 2012/2014 Alternative
for z/OS data type SQL Server and SQL Server
and description 2012/2014 Azure SQL DB 2012/2014
DB2 v.10.5.0 and Data Type and
for LUW Azure SQL DB description Azure SQL
data type Data Type DB Data
Types
7014. The value ‘000E’ 0xff19966f868b11d0b42d
declares the length of the 00c04fc964ff
ROWID column, which is A column or local variable
currently 000E in hex and of uniqueidentifier data
14 in decimal. type can be initialized to a
DB2 can generate a value value in the following ways:
for the column when a row By using the NEWID
is added, depending on function.
the option that you choose By converting from a string
(GENERATED ALWAYS constant in the
or GENERATED BY form xxxxxxxx-xxxx-xxxx-
DEFAULT) when you xxxx-xxxxxxxxxxxx, in
define the column. You which each x is a
can use a ROWID column hexadecimal digit in the
in a table for several range 0-9 or a-f. For
reasons. example, 6F9619FF-8B86-
You can define a ROWID D011-B42D-
column to include LOB 00C04FC964FF is a
data in a table. valid uniqueidentifier value.
You can use direct-row Comparison operators can
access so that DB2 be used
accesses a row directly with uniqueidentifier values
through the ROWID . However, ordering is not
column. If an application implemented by comparing
selects a row from a table the bit patterns of the two
that contains a ROWID values. The only
column, the row ID value operations that can be
implicitly contains the performed against
location of the row. If you a uniqueidentifier value are
use that row ID value in the comparisons (=, <>, <, >,
search condition of <=, >=) and checking for
subsequent SELECT NULL (IS NULL and IS
statements, DB2 might be NOT NULL). No other
able to navigate directly to arithmetic operators can be
the row. used. All column
constraints and properties,
except IDENTITY, can be
used on
the uniqueidentifier data
type.
Merge replication and
transactional replication
with updating subscriptions
use uniqueidentifier column
s to guarantee that rows
are uniquely identified
across multiple copies of
the table.
Guide to Migrating from DB2 to SQL Server and Azure SQL DB Page | 23
Table 4. XML, ROWID data types
DB2 v.10 DB2 Recommended SQL Server 2012/2014 Alternative
for z/OS data type SQL Server and SQL Server
and description 2012/2014 Azure SQL DB 2012/2014
DB2 v.10.5.0 and Data Type and
for LUW Azure SQL DB description Azure SQL
data type Data Type DB Data
Types
* Restriction: ROWID data type can use as table column only in DB2 Version 10 for z/OS. In DB2
Version 10.5.0 for LUW data type ROWID can use only as system information
* Restriction on mapping ROWID data type to uniqueidentifier: ROWID data type uniquely
identifies rows in a DB2® subsystem and represents physical location of the rows. ROWID data
type could be mapped to uniqueidentifier, which is a GUID that could be generated for each row.
Notice that the ROWID value for a particular row in a table might change over time due to a
REORG of the table space. Also you must remember that ROWID the column which implicitly
contains the location of the row it’s not the same as uniqueidentifier which used to uniquely
identify rows in table.
For some applications, you can use the value of a ROWID column to navigate directly to a row. When you
select a ROWID column, the value implicitly contains the location of the retrieved row. If you use the value from
the ROWID column in the search condition of a subsequent query, DB2® can choose to navigate directly to
that row.
For DB2 to be able to use direct row access for the update operation, the SELECT from INSERT statement
and the UPDATE statement must execute within the same unit of work. If these statements execute in different
units of work, the ROWID value for the inserted row might change due to a REORG of the table space before
the update operation. Alternatively, you can use a SELECT from MERGE statement. The MERGE statement
performs INSERT and UPDATE operations as one coordinated statement.
If you define a column in a table to have the ROWID data type, DB2 provides a unique value for each row
in the table only if you define the column as GENERATED ALWAYS. The purpose of the value in the ROWID
column is to uniquely identify rows in the table.
You can use a ROWID column to write queries that navigate directly to a row, which can be useful in
situations where high performance is a requirement. This direct navigation, without using an index or scanning
the table space, is called direct row access. In addition, a ROWID column is a requirement for tables that
contain LOB columns.
Requirement: To use direct row access, you must use a retrieved ROWID value before you commit. When
your application commits, it releases its claim on the table space. After the commit, a REORG on your table
space might execute and change the physical location of the rows.
Restriction: In general, you cannot use a ROWID column as a key that is to be used as a single column
value across multiple tables. The ROWID value for a particular row in a table might change over time due to a
REORG of the table space. In particular, you cannot use a ROWID column as part of a parent key or foreign
key.
The value that you retrieve from a ROWID column is a varying-length character value that is not
monotonically ascending or descending (the value is not always increasing or not always decreasing).
Therefore, a ROWID column does not provide suitable values for many types of entity keys, such as order
numbers or employee numbers.
Guide to Migrating from DB2 to SQL Server and Azure SQL DB Page | 24
2.3.6.3 ROWID columns
There are two different ways of defining a column to be a ROWID data type in a CREATE TABLE
statement:
COLNAME ROWID GENERATED ALWAYS
COLNAME ROWID GENERATED BY DEFAULT
Using the GENERATED ALWAYS keyword, DB2 always generates a ROWID when inserting a row.
Applications and users are not allowed to insert a ROWID.
If you use GENERATED BY DEFAULT, users and applications can supply a value for a ROWID column
as long as the value was previously generated by DB2 and a unique, single column index that exists on the
ROWID column. DB2 checks that the value you are going to insert is a valid ROWID. It is not sufficient to
provide unique numbers yourself. You should only use this parameter when inserting data from another table
for purposes of moving data. The recommended usage is GENERATED ALWAYS. As mentioned above, you
have to create a unique index on the ROWID column when you specify GENERATED BY DEFAULT.
Make sure that there is no way to use the GENERATED ALWAYS clause before implementing
GENERATED BY DEFAULT, because the additional index on a table may increase your response time for
inserting and deleting transactions on the base table. The index is not affected by an UPDATE statement since
the ROWID is not updateable. If you try to update a ROWID column, DB2 issues SQLCODE -151, because the
catalog description indicates that this column cannot be updated.
Attention: When you specify GENERATED BY DEFAULT for a ROWID column, make sure
that a single column unique index exists on your ROWID column. ROWID values can never
contain null values, so the ROWID column has to be defined as NOT NULL.
Be aware that a ROWID column implies some restrictions, preventing the values in the column from being
manipulated:
Users are not allowed to update a ROWID column.
Null values cannot be assigned to ROWID columns.
EDITPROCs, FIELDPROCs and CHECK CONSTRAINTs are not provided for ROWIDs.
It is not allowed to load a single partition or a range of partitions if a column of data type
ROWID is part of the partitioning key.
The ROWID column is stored like a VARCHAR (17) column. In DB2 V7 two different types of
ROWIDs can be defined.
2.4.2 Triggers
Generally, DB2 triggers can be converted to SQL Server triggers. The type of a trigger may need to be
changed; for example, you should convert the BEFORE trigger to INSTEAD OF. Also, some additional code
may need to be added to a trigger, and references to new/old tables and rows should be replaced. For
details, see section 3.2, “Triggers.”
2.4.3 Views
In most cases, views are compatible and problems appear only when converting an underlying SELECT
statement.
Guide to Migrating from DB2 to SQL Server and Azure SQL DB Page | 25
2.4.4 Sequences
In most cases, SEQUENCEs are compatible and problems appear only when converting an PREVIOUS
VALUE expression . For details, see section 3.3, “Sequences.”
2.4.5 Routines
Routines include stored procedures and user-defined functions. To read details about their conversion, see
section 3.5.1, “Procedures” and section 3.5.2, “User-Defined Functions.”
Not all DB2 database objects have direct equivalents in SQL Server. In some cases, SSMA creates
additional objects to provide the proper emulation.
Guide to Migrating from DB2 to SQL Server and Azure SQL DB Page | 26
3.0 DB2 Migration Issues
This section identifies problems that may occur when migrating from DB2 9.x to SQL Server, and suggests
ways to handle those problems.
Links: DB2 for Linux UNIX and Windows 10.5.0, DB2 for z/OS 11.0.0, SQL Server 2014.
These types of tables hold persistent data. There are different kinds of base tables as outlined below.
Regular tables with indexes are the "general purpose" table choice.
These types of tables are implemented as tables that are physically clustered on more than one key, or
dimension, at the same time. MDC tables are used in data warehousing and large database environments.
Clustering indexes on regular tables support single-dimensional clustering of data. MDC tables provide the
benefits of data clustering across more than one dimension. MDC tables provide guaranteed clustering within
the composite dimensions.
MDC introduces indexes that are block-based. "Block indexes" point to blocks or groups of records instead
of to individual records. By physically organizing data in an MDC table into blocks according to clustering
values, and then accessing these blocks using block indexes, MDC is able not only to address all of the
drawbacks of clustering indexes, but to provide significant additional performance benefits.
MDC tables can coexist with partitioned tables and can themselves be partitioned tables.
These types of tables are implemented as sequential clusters of data that provide fast, direct access. Each
record in the table has a predetermined record ID (RID) which is an internal identifier used to locate a record
in a table. RCT tables are used where the data is tightly clustered across one or more columns in the table.
The largest and smallest values in the columns define the range of possible values. You use these columns to
access records in the table; this is the most optimal method of utilizing the predetermined record identifier (RID)
aspect of RCT tables.
Guide to Migrating from DB2 to SQL Server and Azure SQL DB Page | 27
3.1.1.5 Partitioned tables
Partitioned tables use a data organization scheme in which table data is divided across multiple storage
objects, called data partitions or ranges, according to values in one or more table partitioning key columns of
the table.
Just as you can add or attach new partitions to a partitioned table, you can also remove existing partitions.
(Removed partitions become regular, stand-alone base tables.) Partitions can be removed by executing the
ALTER TABLE statement with the DETACH PARTITION option specified.
In DB2 you can globally declare temporary table to temporarily retain some rows for processing by
subsequent SQL statements. A temporary table exists only until the thread is terminated (or sooner). It is not
defined in the DB2 catalog, and neither its definition nor its contents are visible to other users. Multiple users
can declare the same temporary table at the same time, with each independently working with their own copy.
The temporary table name can be any valid DB2 table name. The table qualifier, if provided, must be
SESSION. If the qualifier is not provided, it is assumed to be SESSION. If the temporary table has been
previously defined in this session, the WITH REPLACE clause can be used to override it. Alternatively, one
can DROP the prior instance. An index can be defined on a global temporary table. The SESSION qualifier
must be explicitly provided. Any column type can be used in the table, except for BLOB, CLOB, DBCLOB,
LONG VARCHAR, LONG VARGRAPHIC, DATALINK, reference data types, and structured data types. You
can choose to preserve or delete the rows in the table when a commit occurs (deletion is the default). Deleting
the rows does not drop the table. Standard identity column definitions can be used if desired. Changes are not
logged.
In SQL Server, temporary tables work differently. A temporary table can be either local or global. A local
temporary table is visible only to the user who created that table, and is deleted after the user disconnects. If a
local temporary table is created in a procedure, then it is automatically dropped after the process goes out of
scope of the procedure. Global temporary tables are visible to all users and all sessions. Such tables are
deleted after all users who are referencing them disconnect from the instance of SQL Server. Because global
temporary tables are visible to all, you must use only local temporary tables.
When using local temporary tables in SQL Server, you must pay attention to the scope table, because the
scope table can be used only at the level where it is created, or at deeper levels. In the case of a local table
with a higher level, it will not be visible and an error message will result. On a deeper level, you can create a
new local temporary table with the same name and apply the statements directly to this table, but in that case
the previously created local temporary table becomes inaccessible.
Links: DB2 for Linux UNIX and Windows 10.5.0, DB2 for z/OS 11.0.0, SQL Server 2014.
Here is a DB2 example of declaring a global temporary table by listing the columns.
DB2 Example:
CREATE PROCEDURE DB2_TABLES.DECL_GLOBAL_TEMP_TAB
BEGIN
DECLARE GLOBAL TEMPORARY TABLE SESSION.TEMP_EMP
(EMPNO CHAR(6) NOT NULL,
SALARY DECIMAL(9, 2),
BONUS DECIMAL(9, 2),
COMM DECIMAL(9, 2))
ON COMMIT PRESERVE ROWS;
Guide to Migrating from DB2 to SQL Server and Azure SQL DB Page | 28
INSERT INTO SESSION.TEMP_EMP VALUES (1,1000,100,100);
END;
CALL DB2_TABLES.DECL_GLOBAL_TEMP_TAB;
Solution:
In SQL Server, generally you should not create a local temporary table in the same place where it is
declared in DB2. You should create it as early as possible, preferably at the very beginning of the session. With
this approach, the table scope will be broad enough to avoid the danger of destroying the table before some
other code can reference it.
To convert from DB2, replace DECLARE with CREATE, and change the TEMPORARY keyword to a single
pound sign (#) before the table name. Omit the database name.
DB2 Example:
CREATE PROCEDURE DB2_TABLES.DECL_TEMP_TAB_OPTIONS
BEGIN
DECLARE GLOBAL TEMPORARY TABLE TEMP_OPTIONS
(IDENTITY_2 INT NOT NULL GENERATED ALWAYS AS IDENTITY (START WITH 0,
INCREMENT BY 1, CACHE 7, MINVALUE 0, MAXVALUE 1000, CYCLE, ORDER),
CLOB_V CLOB WITH DEFAULT EMPTY_CLOB(),
DBCLOB_V DBCLOB WITH DEFAULT EMPTY_DBCLOB(),
BLOB_V BLOB WITH DEFAULT EMPTY_BLOB(),
NCLOB_V NCLOB WITH DEFAULT EMPTY_NCLOB(),
USER_DEFAULT VARCHAR(100) DEFAULT 'ANNA',
SYS_DEF_DATE DATE DEFAULT,
SYS_DEF_VARCH VARCHAR (100) DEFAULT,
SYS_DEF_INT INTEGER DEFAULT,
CURRENT_DATE_V DATE WITH DEFAULT CURRENT_DATE);
END;
Guide to Migrating from DB2 to SQL Server and Azure SQL DB Page | 29
declared temporary table. A typed table or typed view cannot be specified. EXCLUDING COLUMN DEFAULTS
option specify that column defaults are not copied from the source result table definition.
DB2 Example:
DECLARE GLOBAL TEMPORARY TABLE SESSION.FRED
LIKE STAFF EXCLUDING COLUMN DEFAULTS
ON COMMIT PRESERVE ROWS;
Solution:
In SQL Server, you can emulate a temporary table with defined columns by using the statement SELECT *
INTO.
In DB2, in a temporary table, set INCLUDING COLUMN DEFAULTS to specify that the column defaults for
each updatable column of the source result table definition are copied. Columns that are not updatable will not
have a default defined in the corresponding column of the created table.
DB2 Example:
DECLARE GLOBAL TEMPORARY TABLE SESSION.FRED
LIKE STAFF INCLUDING COLUMN DEFAULTS
ON COMMIT PRESERVE ROWS;
Solution:
In SQL Server, you can emulate a temporary table with defined columns by using the statement CREATE
TABLE and defaults for the columns.
3.1.1.7.4 Creating a Temporary Table with Columns Returned by the SELECT statement
Here is a DB2 example that shows a temporary table defined to have a set of columns that are returned by
a particular SELECT statement. The statement is not actually run at definition time, so any predicates provided
are irrelevant.
DB2 Example:
DECLARE GLOBAL TEMPORARY TABLE SESSION.FRED AS
(SELECT DEPT
,MAX (ID) AS MAX_ID
,SUM (SALARY) AS SUM_SAL
FROM STAFF
Guide to Migrating from DB2 to SQL Server and Azure SQL DB Page | 30
WHERE NAME <> 'TOM'
GROUP BY DEPT)
DEFINITION ONLY;
Solution:
In SQL Server, you can emulate a temporary table with columns returned by using the statement SELECT …
INTO.
In DB2, an index can be added to a temporary table in order to improve performance and to enforce
uniqueness. In this example, column defaults are copied, as in section 3.8.3.
DB2 Example:
DECLARE GLOBAL TEMPORARY TABLE SESSION.FRED
LIKE STAFF INCLUDING COLUMN DEFAULTS
ON COMMIT PRESERVE ROWS;
SELECT COUNT(*)
FROM SESSION.FRED;
Solution:
In SQL Server, emulation is identical to DB2 when you create an index (except for changing the
TEMPORARY keyword to a pound sign before the table name, as noted above).
Guide to Migrating from DB2 to SQL Server and Azure SQL DB Page | 31
SELECT COUNT(*)
FROM #FRED;
In DB2, you must drop a temporary table to reuse the name of that table.
DB2 Example:
DECLARE GLOBAL TEMPORARY TABLE SESSION.FRED
(DEPT SMALLINT NOT NULL
,AVG_SALARY DEC (7,2) NOT NULL
,NUM_EMPS SMALLINT NOT NULL)
ON COMMIT PRESERVE ROWS;
/
INSERT INTO SESSION.FRED
SELECT DEPT
,AVG(SALARY)
,COUNT(*)
FROM STAFF
GROUP BY DEPT;
/
SELECT COUNT(*)
FROM SESSION.FRED;
/
DROP TABLE SESSION.FRED;
/
DECLARE GLOBAL TEMPORARY TABLE SESSION.FRED
(DEPT SMALLINT NOT NULL)
ON COMMIT DELETE ROWS;
/
SELECT COUNT(*)
FROM SESSION.FRED;
Solution:
In SQL Server, emulation is identical to all earlier examples in this section. Here, you must remove the local
temporary table before you create a new local temporary table of the same name.
SELECT COUNT(*)
FROM #FRED;
Guide to Migrating from DB2 to SQL Server and Azure SQL DB Page | 32
CREATE TABLE #FRED
(DEPT SMALLINT NOT NULL)
SELECT COUNT(*)
FROM #FRED
In DB2, in the case that a declared global temporary table already exists with the specified name, you can
set WITH REPLACE to specify that the existing table is replaced with the temporary table defined by this
statement (and that all rows of the existing table are deleted).
DB2 Example:
DECLARE GLOBAL TEMPORARY TABLE SESSION.FRED
(DEPT SMALLINT NOT NULL
,AVG_SALARY DEC(7,2) NOT NULL
,NUM_EMPS SMALLINT NOT NULL)
ON COMMIT PRESERVE ROWS WITH REPLACE;
Solution:
In SQL Server, you should first check whether this table already exists, and if it does, you should drop and re-
create it as shown in the examples of sections 3.8.1 through 3.8.6.
In DB2, you can set ON COMMIT DELETE ROWS to specify that all rows of the table will be deleted if no
WITH HOLD cursor is open on the table. ON COMMIT DELETE ROWS is default option of this statement.
DB2 Example:
DECLARE GLOBAL TEMPORARY TABLE SESSION.FRED
(DEPT SMALLINT NOT NULL
,AVG_SALARY DEC(7,2) NOT NULL
,NUM_EMPS SMALLINT NOT NULL)
ON COMMIT DELETE ROWS;
Solution:
In SQL Server, to emulate this option you must before every COMMIT statement write the following code:
Guide to Migrating from DB2 to SQL Server and Azure SQL DB Page | 33
3.1.1.7.9 CREATE GLOBAL TEMPORARY TABLE statement
Links: DB2 for Linux UNIX and Windows 10.5.0, DB2 for z/OS 11.0.0, SQL Server 2014.
The CREATE GLOBAL TEMPORARY TABLE statement creates a description of a temporary table at the
current server. Each session that selects from a created temporary table retrieves only rows that the same
session has inserted. When the session terminates, the rows of the table associated with the session are
deleted.
DB2 Example:
CREATE GLOBAL TEMPORARY TABLE DB2_TABLES.GLOBAL_TMP_TAB
(TMPDEPTNO CHAR(3) NOT NULL,
TMPDEPTNAME VARCHAR(36) NOT NULL,
TMPMGRNO CHAR(6),
TMPLOCATION CHAR(16));
Solution:
You could use a simple table in MSSQL Server.
Links: DB2 for Linux UNIX and Windows 10.5.0, DB2 for z/OS 11.0.0, SQL Server 2014.
Materialized Query Tables, or MQTs can be used to greatly improve the elegance and efficiency of
DB2-based data warehouses. (Of course, MQTs are not solely for data warehousing, but they are most useful
for static data.)
An MQT can be thought of as a view whose data is physically stored instead of virtually accessed when
needed. Each MQT is defined as a SQL query, similar to a view. But the MQT pre-computes the query results
and stores the data. Subsequent user queries that require the data can re-use the data from the MQT instead
of re-computing it, which can save time and resources.
A summary table is a specialized type of materialized query table.
After the SELECT statement, there are several parameters that define the nature of the MQT. First of
all, when you create an MQT there are several options available to specify how the data is to be populated and
refreshed into the MQT. These parameters are:
DATA INITIALLY DEFERRED - Data is not inserted into the table as part of the CREATE TABLE
statement. A REFRESH TABLE statement specifying the table-name is used to insert data into the table.
REFRESH - Indicates how the data in the table is maintained.
DEFERRED - The data in the table can be refreshed at any time using the REFRESH TABLE
statement. The data in the table only reflects the result of the query as a snapshot at the time the REFRESH
TABLE statement is processed. System-maintained materialized query tables defined with this attribute do not
allow INSERT, UPDATE, or DELETE statements. User-maintained materialized query tables defined with this
attribute do allow INSERT, UPDATE, or DELETE statements.
DB2 Example:
CREATE TABLE DB2_OBJECTS.A (A BIGINT, B VARCHAR(100));
IMMEDIATE - The changes made to the underlying tables as part of a DELETE, INSERT, or UPDATE are
cascaded to the materialized query table. In this case, the content of the table, at any point-in-time, is the same
as if the specified subselect is processed. Materialized query tables defined with this attribute do not allow
INSERT, UPDATE, or DELETE.
DB2 Example:
CREATE TABLE DB2_OBJECTS.MQT_AI AS (
SELECT COUNT (*) AS CNT, B
FROM DB2_OBJECTS.A
GROUP BY B)
DATA INITIALLY DEFERRED REFRESH IMMEDIATE;
Solution
You could use SQL Server Indexed views instead of Materialized Query Tables.
MAINTAINED BY SYSTEM: indicates that the MQT is maintained by the system. This option is the
default and it means that the MQT does not allow LOAD, INSERT, UPDATE, or DELETE, or SELECT FOR
UPDATE statements. The REFRESH TABLE statement is used to populate data in the MQT.
MAINTAINED BY USER - The data in the materialized query table is maintained by the user. The user
is allowed to perform update, delete, or insert operations against user-maintained materialized query tables.
The REFRESH TABLE statement, used for system-maintained materialized query tables, cannot be invoked
against user-maintained materialized query tables. Only a REFRESH DEFERRED materialized query table
can be defined as MAINTAINED BY USER.
DB2 Example:
CREATE TABLE DB2_OBJECTS.MQT_MU AS (
SELECT SUM (A) AS SM, B
FROM DB2_OBJECTS.A
GROUP BY B)
DATA INITIALLY DEFERRED REFRESH DEFERRED
ENABLE QUERY OPTIMIZATION
MAINTAINED BY USER;
SET INTEGRITY FOR DB2_OBJECTS.MQT_MU ALL IMMEDIATE UNCHECKED;
Guide to Migrating from DB2 to SQL Server and Azure SQL DB Page | 35
3.1.1.8.1 Materialized query table restrictions
The “fullselect” statements that form part of the definition of materialized query tables (MQTs) is
subject to the following restrictions.
• Every select element must have a name.
• A fullselect must not reference any of the following object types:
materialized query tables,
staging tables,
declared global temporary tables,
created global temporary tables,
typed tables,
system catalog tables,
views that violate any MQT restrictions,
protected tables,
nicknames that are created with the DISALLOW CACHING clause of the CREATE NICKNAME or
ALTER NICKNAME statements,
views that directly or indirectly depend on protected tables.
• A fullselect must not contain any column references or expressions of the following data types:
LOB,
LONG,
DATALINK,
XML,
reference,
user defined structured type,
any distinct type that is based on these data types.
•A fullselect must not contain any column references or expressions or functions that:
depend on the physical characteristics of the data. For example, DBPARTITIONNUM,
HASHEDVALUE, and RID_BIT, RID.
depend on changes to the data. For example, a row change expression or a row change
timestamp column.
are defined as EXTERNAL ACTION.
are defined as LANGUAGE SQL, CONTAINS SQL, READS SQL DATA, or MODIFIES SQL
DATA.
• A fullselect must not include a CONNECT BY clause.
• When MAINTAINED BY FEDERATED_TOOL is specified in the CREATE TABLE statement, the SELECT
clause must not contain a reference to a base table.
• When REFRESH IMMEDIATE is specified:
the CREATE MQT statement must not contain duplicate grouping sets.
at least one unique key from each table that is referenced must be in the select list.
the fullselect must be a subselect. The exception is that UNION ALL is supported in the input table
expression of a GROUP BY clause.
the input table expressions of a UNION ALL or a JOIN must not contain aggregate functions.
• When REFRESH IMMEDIATE is specified, the fullselect must not contain:
a reference to a nickname.
a SELECT DISTINCT statement.
a reference to a special register.
a built-in function that depends on the value of a special register.
a reference to a global variable.
functions that are not deterministic.
OLAP functions.
sampling functions.
text functions.
Guide to Migrating from DB2 to SQL Server and Azure SQL DB Page | 36
any expressions that use the result of aggregate functions.
an aggregate function without the fullselect also containing a GROUP BY clause.
a recursive common table expression.
subqueries.
• When REFRESH IMMEDIATE is specified, and the fullselect contains a GROUP BY clause:
the select list must contain COUNT() or COUNT_BIG().
for each nullable column C, if the select list contains SUM(C), then COUNT(C) is also required.
you must include the SUM(), or GROUPING() aggregate function. No other aggregate function can
be included.
the HAVING clause must not be specified.
in a partitioned database environment, the GROUP BY columns must contain the partitioning key
of the materialized query table.
nesting of aggregate functions is not allowed.
• When REFRESH IMMEDIATE is specified, and the FROM clause references more than one table, only an
inner join, without using the explicit INNER JOIN syntax, is supported.
• When REPLICATED is specified:
aggregate functions and the GROUP BY clause are not allowed.
the MQT must reference only a single table. It cannot include a join, union, or subquery.
the PARTITIONING KEY clause must not be specified.
unique indexes are not allowed for system maintained MQTs.
Links: DB2 for Linux UNIX and Windows 10.5.0, DB2 for z/OS 11.0.0, SQL Server 2014.
Typed tables are used to store instances of objects whose characteristics are defined with the CREATE
TYPE statement. You can create a typed table using a variant of the CREATE TABLE statement.
Because typed tables contain objects that can be referenced by other objects, every typed table has
an object identifier column as its first column. You can name the object identifier column using the REF IS …
USER GENERATED clause. In this case, the column is named Oid. The USER GENERATED part of the REF
IS clause indicates that you must provide the initial value for the object identifier column of each newly inserted
row. It is common practice in object-oriented design to completely separate the data from the object identifier.
For that reason, you cannot update the value of the object identifier after you insert the object identifier.
DB2 Example:
CREATE TYPE DB2_OBJECTS.PERSON_T AS (NAME VARCHAR(20), AGE INT, DOB DATE)
INSTANTIABLE REF USING VARCHAR(13) FOR BIT DATA MODE DB2SQL;
You can also create a hierarchy of typed tables that is based on a hierarchy of structured types. To
store instances of subtypes in database tables, you must create a corresponding table hierarchy.
Guide to Migrating from DB2 to SQL Server and Azure SQL DB Page | 37
The Person typed table is of type Person_t. To store instances of the subtypes of employees and
students, it is necessary to create the subtables of the Person table, Employee and Student. The two additional
subtypes of Employee_t also require tables. Those subtables are named Manager and Architect. Just as a
subtype inherits the attributes of its supertype, a subtable inherits the columns of its supertable, including the
object identifier column. A subtable must reside in the same schema as its supertable.
Rows in the Employee subtable, therefore, will have a total of seven
columns: Oid, Name, Age, Address, SerialNum, Salary, and Dept.
A SELECT, UPDATE, or DELETE statement that operates on a supertable automatically operates on
all its subtables as well. For example, an UPDATE statement on the Employee table might affect rows in
the Employee, Manager, and Architect tables, but an UPDATE statement on the Manager table can only
affect Manager rows.
DB2 Example:
CREATE TABLE DB2_OBJECTS.BUSINESSUNIT OF DB2_OBJECTS.BUSINESSUNIT_T (REF
IS OID USER GENERATED);
Guide to Migrating from DB2 to SQL Server and Azure SQL DB Page | 38
3.1.1.10 LIKE Clause
In DB2, the LIKE clause specifies that the columns of the created table have exactly the same name and
description as the columns of the identified source table, view, or nickname. The name specified after LIKE
must identify a table, view, nickname, or temporary table. The LIKE clause can also be used for defining
default constraints and identity columns in the target table. This is controlled by the clauses
INCLUDING/EXCLUDING COLUMN DEFAULTS and INCLUDING/EXCLUDING IDENTITY COLUMN
ATTRIBUTES.
DB2 Example 1:
CREATE TABLE T1
( C1 INTEGER NOT NULL GENERATED ALWAYS AS IDENTITY
( START WITH 1 INCREMENT BY 1),
C2 VARCHAR(50) WITH DEFAULT 'A');
DB2 Example 2:
CREATE TABLE T1
(C1 INTEGER NOT NULL GENERATED ALWAYS AS IDENTITY
( START WITH 1 INCREMENT BY 1),
C2 VARCHAR(50) WITH DEFAULT 'A');
Solution:
SQL Server does not have similar functionality. Use the default CREATE TABLE or SELECT…INTO
statement to create a table that will have columns with the same name and description as the columns of
another table. The SQL Server statement SELECT…INTO allows you to create IDENTITY fields but does not
allow you to add defaults and calculated fields. Thus you can convert SELECT…INTO statements, which do
not contain the clause INCLUDING/EXCLUDING COLUMN DEFAULTS.
CREATE TABLE T2
( C1 INT IDENTITY(1,1),
C2 VARCHAR(50) DEFAULT 'A');
Links: DB2 for Linux UNIX and Windows 10.5.0, DB2 for z/OS 11.0.0, SQL Server 2014.
Guide to Migrating from DB2 to SQL Server and Azure SQL DB Page | 39
You could use row compression only, value compression only, or both types of compression.
Row compression will almost always yield benefits in terms of storage savings, as it attempts to replace
data patterns that span multiple columns within a row with shorter symbol strings.
In DB2, the COMPRESS clause specifies whether data compression applies to the rows of the table.
DB2 Example:
CREATE TABLE DB2_OBJECTS.COMPRESS_TAB
(C1 INT, C2 VARCHAR(50) )
COMPRESS YES
Solution:
There is no similar clause in SQL Server. To compress data in a SQL Server table, use the WITH
DATA_COMPRESSION = page statement.
Links: DB2 for Linux UNIX and Windows 10.5.0, DB2 for z/OS 11.0.0, SQL Server 2014.
Value compression can offer savings when you have a many rows with columns that contain the same
value, or when you have columns that contain the default value for the data type of the column.
In DB2, the VALUE COMPRESSION clause determines the row format that is to be used. Each data type
has a different byte count, depending on the row format that is used. The NULL value is stored using three
bytes. Whether a column is defined as nullable has no effect on the row size calculation. The zero-length data
values for columns whose data type is VARCHAR, VARGRAPHIC, LONG VARCHAR, LONG VARGRAPHIC,
CLOB, DBCLOB, BLOB, or XML are to be stored using two bytes only, which is less than the storage required
when VALUE COMPRESSION is not active.
DB2 Example:
CREATE TABLE DB2_OBJECTS.VALUE_COMPRESS
(C1 INT, C2 VARCHAR(50) )
VALUE COMPRESSION
Solution:
In SQL Server, you can emulate this clause by using the WITH DATA_COMPRESSION = row statement.
Guide to Migrating from DB2 to SQL Server and Azure SQL DB Page | 40
When value compression is enabled, you can also specify that columns that assume the system default
value for their data types can be further compressed with the COMPRESS SYSTEM DEFAULT option.
DB2 Example:
CREATE TABLE DB2_OBJECTS.VAL_SYS_COMPRESS
(DEPTNO CHAR(3) NOT NULL,
DEPTNAME VARCHAR(36) NOT NULL,
EMPNO CHAR(6) NOT NULL,
SALARY DECIMAL(9,2) NOT NULL WITH DEFAULT COMPRESS SYSTEM DEFAULT)
VALUE COMPRESSION;
Solution:
In SQL Server, you can emulate this clause by using the WITH DATA_COMPRESSION = row statement.
DB2 Example:
CREATE TABLE DB2_OBJECTS.BOTH_COMPRESS
(DEPTNO CHAR(3) NOT NULL,
DEPTNAME VARCHAR(36) NOT NULL)
VALUE COMPRESSION COMPRESS YES;
Solution:
In SQL Server, you can emulate this clause by using the WITH DATA_COMPRESSION = page statement.
In DB2, the CCSID clause specifies the encoding scheme for string data stored in the table. If the CCSID
clause is not specified, the default is CCSID UNICODE for Unicode databases, and CCSID ASCII for all other
databases.
ASCII specifies that string data is encoded in the database code page. If the database is a Unicode
database, CCSID ASCII cannot be specified.
UNICODE specifies that string data is encoded in Unicode. If the database is a Unicode database, character
data is in UTF-8, and graphic data is in UCS-2. If the database is not a Unicode database, character data is
in UTF-8.
Guide to Migrating from DB2 to SQL Server and Azure SQL DB Page | 41
DB2 Example 1:
CREATE TABLE DB2_TABLES.CCSID_ASCII (C1 INT, C2 VARCHAR(50) ) CCSID ASCII;
DB2 Example 2:
CREATE TABLE DB2_TABLES.CCSID_UNICODE(C1 INT, C2 VARCHAR(50)) CCSID
UNICODE;
Solution:
SQL Server supports NCHAR, NVARCHAR, and NTEXT data types for storing Unicode character data.
3.1.1.14 Generated-clause
GENERATED
Specifies that DB2 generates values for the column. GENERATED must be specified if the column is to be
considered an identity column or a row change timestamp column, row-begin column, row-end column,
transaction-start-ID column, or generated expression column.
ALWAYS
Specifies that a value will always be generated for the column when a row is inserted into the table, or whenever
the result value of the generation-expression changes. The result of the expression is stored in the table.
GENERATED ALWAYS is the recommended value unless data propagation or unload and reload operations
are being done. GENERATED ALWAYS is the required value for generated columns.
Links: DB2 for Linux UNIX and Windows 10.5.0, DB2 for z/OS 11.0.0, SQL Server 2014.
DB2 Example:
CREATE TABLE DB2_TABLES.GEN_ALW_STMT (ID INT, GENERATED_EXPRESSION
VARCHAR(100) GENERATED ALWAYS AS (CASE WHEN 1=1 THEN 'WORLD' ELSE
'NOTHING' END));
Solution:
You could replace GENERATED ALWAYS AS clause with SQL Server Computed Columns.
.
SQL Server Example :
CREATE TABLE GEN_ALW_ST (ID INT, GENERATED_EXPRESSION AS
(CASE WHEN 1=1 THEN 'WORLD' ELSE 'NOTHING' END))
System default - special register value as the default for this column when no other value is provided.
Links: DB2 for Linux UNIX and Windows 10.5.0, DB2 for z/OS 11.0.0, SQL Server 2014
DB2 Example:
Guide to Migrating from DB2 to SQL Server and Azure SQL DB Page | 42
CREATE TABLE DB2_TABLES.SYSTEM_DEFAULT (ID INT, SYS_DEF_DATE DATE DEFAULT,
SYS_DEF_VARCH VARCHAR(100) DEFAULT, SYS_DEF_INT INTEGER DEFAULT);
Solution:
You could replace DEFAULT clause with SQL Server DEFAULT clause plus value by default depending on
column data type: DATE equal to GETDATE(),VARCHAR equal to ’’, INTEGER equal to 0.
BY DEFAULT
Specifies that DB2 will generate a value for the column when a row is inserted, or updated specifying the
DEFAULT clause, unless an explicit value is specified. BY DEFAULT is the recommended value when using
data propagation or performing an unload and reload operation.
Although not explicitly required, to ensure uniqueness of the values, define a unique single-column index on
generated IDENTITY columns.
DB2 Example:
CREATE TABLE DB2_TABLES.GEN_ALW_ST (ID INT, GENERATED_EXPRESSION INT
GENERATED BY DEFAULT AS IDENTITY (START WITH 0, INCREMENT BY 1));
Solution:
You could replace GENERATED BY DEFAULT AS IDENTITY clause with SQL Server IDENTITY clause.
Specifies that the column is to be the identity column for this table. A table can only have a single identity
column (SQLSTATE 428C1). The IDENTITY keyword can only be specified if the data type associated with the
column is an exact numeric type with a scale of zero, or a user-defined distinct type for which the source type
is an exact numeric type with a scale of zero (SQLSTATE 42815). SMALLINT, INTEGER, BIGINT, or DECIMAL
with a scale of zero, or a distinct type based on one of these types, are considered exact numeric types. By
contrast, single- and double-precision floating points are considered approximate numeric data types.
Reference types, even if represented by an exact numeric type, cannot be defined as identity columns.
Links: DB2 for Linux UNIX and Windows 10.5.0, DB2 for z/OS 11.0.0, SQL Server 2014.
DB2 Example:
CREATE TABLE DB2_TABLES.IDENT (ID INT, IDENT INTEGER NOT NULL GENERATED
ALWAYS AS IDENTITY (START WITH 0, INCREMENT BY 1, NO CACHE, NO MINVALUE,
MAXVALUE 0, NO CYCLE, NO ORDER));
Solution:
You could replace GENERATED ALWAYS AS IDENTITY clause with SQL Server IDENTITY clause.
Guide to Migrating from DB2 to SQL Server and Azure SQL DB Page | 43
SQL Server Example:
CREATE TABLE IDENT (ID INT, IDENT INTEGER NOT NULL IDENTITY (1,1))
Specifies that the column is a timestamp column for the table. A value is generated for the column in each row
that is inserted, and for any row in which any column is updated. The value that is generated for a ROW
CHANGE TIMESTAMP column is a timestamp that corresponds to the insert or update time for that row. If
multiple rows are inserted or updated with a single statement, the value of the ROW CHANGE TIMESTAMP
column might be different for each row.
A table can only have one ROW CHANGE TIMESTAMP column. A ROW CHANGE TIMESTAMP column
cannot have a DEFAULT clause. NOT NULL must be specified for a ROW CHANGE TIMESTAMP column.
DB2 Example:
CREATE TABLE DB2_TABLES.ROW_CHANGE_TIMESTAMP (ID INT, GEN_DEF TIMESTAMP
NOT NULL GENERATED BY DEFAULT FOR EACH ROW ON UPDATE AS ROW CHANGE
TIMESTAMP);
Solution:
For INSERT statement in MS SQL Server you could use DEFAULT CURRENT_TIMESTAMP for column.
For UPDATE statement in MS SQL Server you could use additional column in SET statement and set
appropriate column with CURRENT_TIMESTAMP function.
3.1.1.15 Constraints
Links: DB2 for Linux UNIX and Windows 10.5.0, DB2 for z/OS 11.0.0, SQL Server 2014.
DB2 Example:
CREATE TABLE DB2_TABLES.ON_DEL_NOACT_FK (ID_FK INT NOT NULL PRIMARY KEY,
VAL VARCHAR (100));
Solution:
In SQL Server, the only difference in primary key constraint syntax is that SQL Server does not require the
NOT NULL keyword to be added.
Guide to Migrating from DB2 to SQL Server and Azure SQL DB Page | 44
Links: DB2 for Linux UNIX and Windows 10.5.0, DB2 for z/OS 11.0.0, SQL Server 2014.
ON DELETE;
Specifies what action is to take place on the dependent tables when a row of the parent table is deleted. There
are four possible actions:
NO ACTION (default);
RESTRICT;
CASCADE;
SET NULL.
ON UPDATE.
Specifies what action is to take place on the dependent tables when a row of the parent table is updated. There
are two possible actions:
NO ACTION (default);
RESTRICT.
The delete rule applies when a row of T2 is the object of a DELETE or propagated delete operation and that
row has dependents in T1. Let p denote such a row of T2.
If C or NO ACTION is specified, an error occurs and no rows are deleted.
If CASCADE is specified, the delete operation is propagated to the dependents of p in T1.
If SET NULL is specified, each nullable column of the foreign key of each dependent of p in T1 is set
to null.
The use of NO ACTION or RESTRICT as delete or update rules for referential constraints determines when
the constraint is enforced. A delete or update rule of RESTRICT is enforced before all other constraints,
including those referential constraints with modifying rules such as CASCADE or SET NULL. A delete or update
rule of NO ACTION is enforced after other referential constraints. One example where different behavior is
evident involves the deletion of rows from a view that is defined as a UNION ALL of related tables.
Solution:
In SQL Server you could use ON DELETE { NO ACTION | CASCADE | SET NULL | SET DEFAULT } statement
which specifies what action happens to rows in the table created, if those rows have a referential relationship
and the referenced row is deleted from the parent table. The default is NO ACTION.
DB2 Example:
ON DELETE; NO ACTION (DEFAULT);
DB2 Example:
ON DELETE; RESTRICT;
Solution:
NO ACTION
The Database Engine raises an error and the delete action on the row in the parent table is rolled back.
Guide to Migrating from DB2 to SQL Server and Azure SQL DB Page | 45
SQL Server Example:
CREATE TABLE DBO.ON_DEL_NOACT_PK (ID INT, ID_FK INT, VAL VARCHAR (100),
CONSTRAINT FK_NOACT FOREIGN KEY (ID_FK) REFERENCES DBO.ON_DEL_NOACT_FK ON
DELETE NO ACTION);
CREATE TABLE DBO.ON_DEL_NOACT_FK (ID_FK INT NOT NULL PRIMARY KEY, VAL
VARCHAR (100));
DB2 Example:
ON DELETE; CASCADE;
Solution:
CASCADE
Corresponding rows are deleted from the referencing table if that row is deleted from the parent table.
DB2 Example:
ON DELETE; SET NULL;
Solution:
SET NULL
All the values that make up the foreign key are set to NULL if the corresponding row in the parent table is
deleted. For this constraint to execute, the foreign key columns must be nullable.
In DB2 ON UPDATE - Specifies what action is to take place on the dependent tables when a row of the parent
table is updated. The clause is optional. ON UPDATE NO ACTION is the default and ON UPDATE RESTRICT
is the only alternative. In MS SQL Server you could use ON UPDATE { NO ACTION } – statement.
Guide to Migrating from DB2 to SQL Server and Azure SQL DB Page | 46
3.1.2.1 WITH LOCAL CHECK OPTION Clause
In DB2, the WITH LOCAL CHECK OPTION constraint on a view means that the search condition of the view
is applied as a constraint for an insert or update of the view, or for any other view that is dependent on this
view. In contrast to a view with the cascaded check option, a view with the local check option doesn’t inherit
the search conditions as constraints from any updatable view on which it is dependent.
Links: DB2 for Linux UNIX and Windows 10.5.0, DB2 for z/OS 11.0.0, SQL Server 2014.
DB2 Example:
CREATE VIEW V2 AS SELECT ID, V FROM V1 WHERE ID < 5 WITH LOCAL CHECK
OPTION;
Solution:
SQL Server supports only views with the cascaded check option. Create the view without the check option,
and INSTEAD OF, INSERT, UPDATE, or DELETE triggers on it. Then implement the check in the triggers by
raising errors when the condition is false.
DB2 uses the CLUSTER keyword to create a clustered index, which is incompatible with SQL Server.
DB2 Example:
CREATE TABLE DB2_TABLES.EMPLOYEES_INDX (
ID DECIMAL(5,0),
NAME VARCHAR(50),
LAST_NAME VARCHAR(50),
DOB DATE,
DEPT VARCHAR(10),
SAL DECIMAL(5,0),
JOB VARCHAR(10));
Solution:
Replace the CLUSTER keyword with CLUSTERED. There is also a slight difference in the syntax of
creating clustered indexes.
Guide to Migrating from DB2 to SQL Server and Azure SQL DB Page | 47
CREATE CLUSTERED INDEX ID_CLUSTER ON DBO.EMPLOYEES_INDX (ID);
In DB2, you can leave a percentage of each index page as free space when building the index.
DB2 Example:
CREATE TABLE DB2_TABLES.EMP_INDX_PSTFREE_REVERS (ID DECIMAL, NAME
VARCHAR(50), LAST_NAME VARCHAR(50), DOB DATE, DEPT VARCHAR (10), SAL
DECIMAL);
Solution:
In SQL Server, you can emulate this feature by using the FILLFACTOR keyword and a value of the
filled percentage of each index page (the reverse percentage of the PCTFREE clause in DB2).
DB2 includes a set of COLLECT STATISTICS clauses that specify which basic index statistics are to
be collected during index creation.
COLLECT DETAILED STATISTICS specifies that extended index statistics are also to be collected
during index creation. COLLECT SAMPLED DETAILED STATISTICS specifies that sampling can be used
when compiling extended index statistics.
DB2 Example 1:
CREATE TABLE DB2_TABLES.EMP_INDX_COLLECT_STAT (ID DECIMAL, NAME
VARCHAR(50), LAST_NAME VARCHAR(50), DOB DATE, DEPT VARCHAR (10), SAL
DECIMAL);
DB2 Example 2:
CREATE INDEX DB2_TABLES.IDX2 ON DB2_TABLES.EMP_INDX_COLLECT_STAT (ID)
COLLECT DETAILED STATISTICS;
DB2 Example 3:
CREATE INDEX DB2_TABLES.IDX3 ON DB2_TABLES.EMP_INDX_COLLECT_STAT (ID)
COLLECT SAMPLED DETAILED STATISTICS;
Solution:
This clause can be partially emulated in SQL Server using the WITH STATISTICS_NORECOMPUTE
statement.
Guide to Migrating from DB2 to SQL Server and Azure SQL DB Page | 48
CREATE INDEX IDX1 ON DBO.EMP_INDX_COLLECT_STAT (ID) WITH
(STATISTICS_NORECOMPUTE = OFF);
In DB2, for indexes on XML data, the UNIQUE keyword enforces uniqueness within a single XML
column across all documents whose nodes are qualified by the XML pattern.
DB2 Example:
CREATE TABLE DB2_TABLES.COMPANY_XML_IND (ID INT NOT NULL, XML_COLUMN XML
NOT NULL);
CREATE UNIQUE INDEX DB2_TABLES.XML_IND ON
DB2_TABLES.COMPANY_XML_IND(XML_COLUMN) GENERATE KEY USING XMLPATTERN
'/COMPANY/EMP/NAME/LAST' AS SQL VARCHAR(100);
Solution:
SQL Server table needs to have a clustered primary key to create a primary XML index on it. The
SQL Server statement PRIMARY XML INDEX contains the primary key of the parent table, so the unique
requirement is always achieved automatically.
Indexing of XML data depends on which pattern expression the data has. The XML pattern affects
what exactly will be indexed: the paths or the nodes of the XML document. To index on an XML pattern, you
provide the index specification clause GENERATE KEY USING XMLPATTERN during index creation.
DB2 Example 1:
@ - Specifies attributes of the context node. This is the abbreviated syntax for attribute::.
CREATE INDEX DB2_TABLES.EMPINDEX ON DB2_TABLES.COMPANY_XML_IND(XML_COLUMN)
GENERATE KEY USING XMLPATTERN '/COMPANY/EMP/@ID' AS SQL DOUBLE;
DB2 Example 2:
child:: - Specifies children of the context node. This is the default, if no other forward axis is specified.
attribute:: - Specifies attributes of the context node.
Guide to Migrating from DB2 to SQL Server and Azure SQL DB Page | 49
CREATE INDEX DB2_TABLES.CHILDINDEX ON
DB2_TABLES.COMPANY_XML_IND(XML_COLUMN) GENERATE KEY USING XMLPATTERN
'/CHILD::COMPANY/CHILD::EMP/ATTRIBUTE::ID' AS SQL DOUBLE;
DB2 Example 3:
// (double forward slash) - this is the abbreviated syntax for /descendant-or-self::node()/.
DB2 Example 4:
descendant-or-self:: - specifies the context node and the descendants of the context node.
node() - matches any node. You cannot use node() if you also specify UNIQUE.
DB2 Example 5:
text() - matches any text node.
CREATE INDEX DB2_TABLES.TEXTINDEX ON
DB2_TABLES.COMPANY_XML_IND(XML_COLUMN) GENERATE KEY USING XMLPATTERN
'/COMPANY/EMP/NAME/LAST/TEXT()' AS SQL VARCHAR(25);
Solution:
In SQL Server, create a secondary XML index using the FOR { VALUE | PATH | PROPERTY } clause.
XML indexes fall into the following categories:
Primary XML index
Secondary XML index
The first index on the xml type column must be the primary XML index. Using the primary XML index, the
following types of secondary indexes are supported: PATH, VALUE, and PROPERTY.
PATH Secondary XML Index - if your queries generally specify path expressions on xml type columns, a PATH
secondary index may be able to speed up the search.
COMPRESS - specifies whether index compression is enabled. By default, index compression will be
enabled if data row compression is enabled; index compression will be disabled if data row compression is
disabled. This option can be used to override the default behavior.
YES - specifies that index compression is enabled. Insert and update operations on the index will be
subject to compression.
NO - specifies that index compression is disabled.
DB2 Example :
CREATE TABLE DB2_TABLES.EMP_INDX_COMPRESS (ID INT, FIELD VARCHAR(50));
Solution:
DATA_COMPRESSION - specifies the data compression option for the specified index, partition
number, or range of partitions. The options are as follows:
You could use PAGE compression - index or specified partitions are compressed by using page
compression.
Guide to Migrating from DB2 to SQL Server and Azure SQL DB Page | 51
3.2 Triggers
This section explains how to convert DB2 triggers to SQL Server triggers.
Links: DB2 for Linux UNIX and Windows 10.5.0, DB2 for z/OS 11.0.0, SQL Server 2014.
Guide to Migrating from DB2 to SQL Server and Azure SQL DB Page | 52
3.2.2 FOR EACH ROW Triggers
DB2 supports the FOR EACH ROW triggers, which are not supported in SQL Server. These triggers
are fired as many times as there are rows changed by the triggering statement.
Inside a DB2 trigger, you can refer to columns in the subject table (the table associated with the trigger) by
using the OLD and NEW aliases that are specified in the REFERENCING clause. The “OLD AS O” O.col_name
refers to a column in an existing row before it is updated or deleted. The “NEW AS N” N.col_name refers to the
column of a new row to be inserted or an existing row after it is updated.
DB2 Example:
CREATE TRIGGER DB2_TABLES.FOR_EACH_ROW AFTER UPDATE ON
DB2_TABLES.AFTER_UPD
REFERENCING NEW AS N OLD AS O
FOR EACH ROW
BEGIN ATOMIC
INSERT INTO DB2_TABLES.AFTER_UPD_INS VALUES (N.ID, O.V || '' || N.V);
END;
Solution:
The functionality offered by the FOR EACH ROW trigger can be emulated by using a SQL Server cursor
in a trigger.
For this solution, you need to add a new column to the table: This column will be used to uniquely identify
the row being updated, so we will name it rowid and assign SQL Server type uniqueidentifier to it. Thus this
column is used to synchronize old and new values of each row.
Guide to Migrating from DB2 to SQL Server and Azure SQL DB Page | 53
END
CLOSE FOREACHINSERTEDROWTRIGGERCURSOR
DEALLOCATE FOREACHINSERTEDROWTRIGGERCURSOR
END
DB2 Example:
CREATE TRIGGER DB2_TABLES.FOR_EACH_ST AFTER UPDATE ON DB2_TABLES.AFTER_UPD
REFERENCING NEW_TABLE AS N OLD_TABLE AS O
FOR EACH STATEMENT
BEGIN ATOMIC
INSERT INTO DB2_TABLES.AFTER_UPD_INS
SELECT N.ID, O.V || ' ' || N.V
FROM N JOIN O ON N.ID=O.ID;
END;
Solution:
The functionality offered by the FOR EACH STATEMENT triggers can be emulated by using INSERTED
and DELETED aliases instead of NEW_TABLE and OLD_TABLE aliases respectively.
DB2 Example:
CREATE TRIGGER DB2_TABLES.BEFORE_TRIGGER BEFORE UPDATE ON
DB2_TABLES.BEFORE_UPD
REFERENCING NEW AS N OLD AS O
FOR EACH ROW
BEGIN ATOMIC
SET N.V=O.V || N.V;
END;
Solution:
BEFORE can be emulated by using a SQL Server INSTEAD OF trigger.
Guide to Migrating from DB2 to SQL Server and Azure SQL DB Page | 54
Note that for this solution, you need to add a new column to the subject table. This column will be used to
uniquely identify the row being updated, so we will name it rowid and assign SQL Server type uniqueidentifier
to it. Thus this column lets us synchronize old and new values of each row. For the INSTEAD OF trigger that
is based on a view, the view should be modified in such a way that a rowid column is present there.
DB2 Example:
CREATE TRIGGER DB2_TABLES.TTR$TR1 BEFORE INSERT ON DB2_TABLES.TTR
REFERENCING NEW AS N OLD AS O FOR EACH ROW
BEGIN
SET N.A= N.A||O.A;
END;
DB2 Example:
CREATE TRIGGER DB2_TABLES.BEF_DEL BEFORE DELETE ON DB2_TABLES.BEF_INS
REFERENCING OLD AS O
FOR EACH ROW
BEGIN
SIGNAL SQLSTATE '75000' SET MESSAGE_TEXT = 'YOU ARE INTEND TO DELETE
ROWS';
END;
DB2 Example:
CREATE TRIGGER DB2_TABLES.IF_INS_DEL_UPD
AFTER INSERT OR DELETE OR UPDATE ON DB2_TABLES.TTR
REFERENCING NEW AS N OLD AS O FOR EACH ROW
BEGIN
IF INSERTING
THEN UPDATE DB2_TABLES.TTR2 SET A = A + 1;
END IF;
IF DELETING
THEN UPDATE DB2_TABLES.TTR2 SET A = A - 1;
END IF;
IF UPDATING
THEN UPDATE DB2_TABLES.TTR2 SET A = A - 10;
END IF;
END;
Solution:
DB2 allow any combination of the events can be specified, but each event (INSERT, DELETE, and UPDATE)
can only be specified once. In SQL Server more than one trigger event is not supported, you could create a
separate trigger for every trigger event.
DB2 Example1:
CREATE TRIGGER DB2_TABLES.BEF_UPD BEFORE UPDATE ON DB2_TABLES.BEFORE_UPD
REFERENCING NEW AS N OLD AS O
FOR EACH ROW WHEN (N.ID=5 AND O.ID=1)
BEGIN ATOMIC
SET N.V=O.V || N.V;
END;
Solution:
In SQL Server, WHEN clause can be emulated by using IF construct.
DB2 Example2:
CREATE TRIGGER DB2_OBJECTS.AFTER_UPD_ST AFTER UPDATE ON
DB2_OBJECTS.AFTER_UPD
REFERENCING NEW_TABLE AS N OLD_TABLE AS O
FOR EACH STATEMENT WHEN ((SELECT COUNT(*) FROM N)>1)
BEGIN ATOMIC
INSERT INTO DB2_OBJECTS.AFTER_UPD_INS
SELECT N.ID, O.V || N.V FROM N JOIN O ON N.ID=O.ID;
END
Guide to Migrating from DB2 to SQL Server and Azure SQL DB Page | 58
3.3 Sequences
Links: DB2 for Linux UNIX and Windows 10.5.0, DB2 for z/OS 11.0.0, SQL Server 2014.
A DB2 SEQUENCE is a user-defined object that generates a series of numeric values based on the
specification with which the SEQUENCE was created. The most common purpose of a SEQUENCE is to
provide unique values for the primary key column of a table. DB2 SEQUENCEs are not associated with tables.
Applications refer to a SEQUENCE object to get the current or next value of that SEQUENCE. DB2 keeps the
set of generated values of a SEQUENCE in a cache, and a unique set of cached values is created for each
session.
In DB2, the NEXTVAL expression generates and returns the next value for the specified SEQUENCE. The
DB2 PREVVAL expression returns the most recently generated value of the previous NEXTVAL expression for
the same SEQUENCE within the current application process. In DB2, the value of the PREVVAL expression
persists until the next value is generated for the SEQUENCE, the SEQUENCE is dropped, or the application
session ends.
Solution:
SQL Server 2014 support objects with functionality similar to that of a DB2 SEQUENCE. In many cases if you
use SEQUENCE only for getting NEXTVAL you can convert it to SQL Server SEQUENCE.
DB2 Example:
CREATE SEQUENCE CUSTOMER_NO AS INTEGER
INSERT INTO CUSTOMERS VALUES
(NEXT VALUE FOR CUSTOMER_NO, 'COMMENT', ...)
However, some features of DB2 SEQUENCEs (e.g. PREVVAL) are not supported in SQL Server. Two
distinct scenarios of DB2 SEQUENCE PREVVAL usage exist: a variable that saves SEQUENCE value, and
an auxiliary table that represents a DB2 SEQUENCE.
SQL Server Scenario 1: Converting a DB2 table with automatically generated primary key
In the first scenario, a SEQUENCE is used to generate single unique value which is used for a few tables. This
is fully compatible with SQL Server usage, and in this case you should modify code like as in example:
DB2 Example:
CREATE SEQUENCE SEQ1 AS INTEGER
...
INSERT INTO T1 (ID, NAME)
VALUES (NEXT VALUE FOR SEQ1, ‘NAME’);
INSERT INTO T2 (ID, NAME)
VALUES (PREVIOUS VALUE FOR SEQ1, ‘NAME’);
...
To maintain such emulation of NEXTVAL, you must clean up the added rows to avoid unrestricted growth
of the auxiliary table. The fastest way to do this in SQL Server is to use a transactional approach:
In SQL Server, IDENTITY is generated in a transaction-independent way and, as in DB2, rolling back the
transaction does not affect the current IDENTITY value. In this scenario, we can emulate PREVVAL by using
SQL Server @@IDENTITY or SCOPE_IDENTITY() functions. @@IDENTITY returns the value for the last
INSERT statement in the session, and SCOPE_IDENTITY() gets the last IDENTITY value assigned within the
scope of current Transact-SQL module. Note that the values returned by these two functions can be overwritten
by next INSERT statement in the current session, so we highly recommend that you save the value in an
intermediate variable, if PREVVAL is used afterwards in the source code. Both @@IDENTITY and
SCOPE_IDENTITY() are limited to the current session scope, which means that as in DB2, the identities
generated by concurrent processes are not visible.
Note: Azure SQL DB doesn’t support sequence objects.
In DB2, the FETCH FIRST clause sets a maximum number of rows that can be retrieved.
Links: DB2 for Linux UNIX and Windows 10.5.0, DB2 for z/OS 11.0.0, SQL Server 2014.
DB2 Example:
SELECT ID, VAL
Guide to Migrating from DB2 to SQL Server and Azure SQL DB Page | 60
FROM (
SELECT ID, VAL
FROM TABLE_FF
ORDER BY ID DESC
FETCH FIRST 10 ROWS ONLY
) A
FETCH FIRST 5 ROW ONLY
Solution:
In SQL Server, convert the FETCH FIRST clause by using the optional <offset_fetch> clause of ORDER BY
in SELECT statement. OFFSET clause is required, but you should set rows count equal to 0. If ORDER BY
clause is missed in DB2, in SQL Server you should generate ORDER BY clause with (SELECT <constant>)
as an order_by_expression.
In DB2, the OPTIMIZE FOR clause requests special processing of the select statement. If the OPTIMIZE
FOR clause is specified, it is assumed that the number of rows retrieved will probably not exceed n, where n
is the value of integer.
Links: DB2 for Linux UNIX and Windows 10.5.0, DB2 for z/OS 11.0.0, SQL Server 2014.
DB2 Example:
SELECT ID, VAL
FROM TABLE_FF
OPTIMIZE FOR 5 ROWS
Solution:
In SQL Server, emulate an OPTIMIZE FOR clause by using the FAST query hint.
DB2 supports two set operators that are incompatible with SQL Server: EXCEPT ALL and INTERSECT ALL.
Guide to Migrating from DB2 to SQL Server and Azure SQL DB Page | 61
Links: DB2 for Linux UNIX and Windows 10.5.0, DB2 for z/OS 11.0.0, SQL Server 2014.
DB2 Example:
SELECT X, Y, Z FROM (VALUES (1, 1, 1), (1, 1, 1), (1, 1, 2), (2, 1, 2),
(2, 1, 2), (2, 1, 1), (3, 1, 1), (4, 1, 1), (4, 1, 1), (5, 1, 1)
) R1 (X, Y, Z)
EXCEPT ALL
SELECT X, Y, Z FROM (VALUES (1, 1, 1), (1, 1, 1), (2, 1, 2), (3, 1, 1),
(3, 1, 2), (3, 2, 1), (3, 1, 3), (4, 2, 1)
) R2 (X, Y, Z)
ORDER BY X, Y, Z
SELECT X, Y, Z FROM (VALUES (1, 1, 1), (1, 1, 1), (1, 1, 2), (2, 1, 2),
(2, 1, 2), (2, 1, 1), (3, 1, 1), (4, 1, 1), (4, 1, 1), (5, 1, 1)
) R1 (X, Y, Z)
EXCEPT
SELECT X, Y, Z FROM (VALUES (1, 1, 1), (1, 1, 1), (2, 1, 2), (3, 1, 1),
(3, 1, 2), (3, 2, 1), (3, 1, 3), (4, 2, 1)
) R2 (X, Y, Z)
ORDER BY X, Y, Z
SELECT X, Y, Z FROM (VALUES (1, 1, 1), (1, 1, 1), (1, 1, 2), (2, 1, 2),
(2, 1, 2), (2, 1, 1), (3, 1, 1), (4, 1, 1), (4, 1, 1), (5, 1, 1)
) R1 (X, Y, Z)
INTERSECT ALL
SELECT X, Y, Z FROM (VALUES (1, 1, 1), (1, 1, 1), (2, 1, 2), (3, 1, 1),
(3, 1, 2), (3, 2, 1), (3, 1, 3), (4, 2, 1)
) R2 (X, Y, Z)
ORDER BY X, Y, Z
SELECT X, Y, Z FROM (VALUES (1, 1, 1), (1, 1, 1), (1, 1, 2), (2, 1, 2),
(2, 1, 2), (2, 1, 1), (3, 1, 1), (4, 1, 1), (4, 1, 1), (5, 1, 1)
) R1 (X, Y, Z)
INTERSECT
SELECT X, Y, Z FROM (VALUES (1, 1, 1), (1, 1, 1), (2, 1, 2), (3, 1, 1),
(3, 1, 2), (3, 2, 1), (3, 1, 3), (4, 2, 1)
) R2 (X, Y, Z)
ORDER BY X, Y, Z
Solution:
Guide to Migrating from DB2 to SQL Server and Azure SQL DB Page | 62
In SQL Server, emulate INTERSECT ALL and EXCEPT ALL by using an additional numeric column
(Tmp$Num) with INTERSECT and EXCEPT as shown:
SELECT <SELECT_COLUMNS_OR_ALIAS> FROM (
SELECT <FIRST_SELECT_COLUMNS_WITH_ALIAS>,
ROW_NUMBER() OVER(PARTITION BY
<FIRST_SELECT_COLUMNS_WITHOUT_ALIAS> ORDER BY (SELECT 1)
) AS TMP$NUM
FROM ...
{ INTERSECT | EXCEPT }
SELECT <SECOND_SELECT_COLUMNS_WITH_ALIAS>,
ROW_NUMBER() OVER(PARTITION BY
<SECOND_SELECT_COLUMNS_WITHOUT_ALIAS> ORDER BY (SELECT 1)
) AS TMP$NUM
FROM ...
) <SUB QUERY TABLE NAME>
All duplicate rows are numbered in both SELECT statements in the new column Tmp$Num. This set no
longer contains duplicates, so you can now use INTERSECT or EXCEPT. Then you can select the result
without the Tmp$Num column.
SELECT X, Y, Z FROM (VALUES (1, 1, 1), (1, 1, 1), (1, 1, 2), (2, 1, 2),
(2, 1, 2), (2, 1, 1), (3, 1, 1), (4, 1, 1), (4, 1, 1), (5, 1, 1)
) R1 (X, Y, Z)
EXCEPT
SELECT X, Y, Z FROM (VALUES (1, 1, 1), (1, 1, 1), (2, 1, 2), (3, 1, 1),
(3, 1, 2), (3, 2, 1), (3, 1, 3), (4, 2, 1)
) R2 (X, Y, Z)
ORDER BY X, Y, Z
SELECT X, Y, Z FROM (
SELECT X, Y, Z,
ROW_NUMBER() OVER(PARTITION BY X, Y, Z ORDER BY (SELECT 1)
) AS TMP$NUM
FROM (VALUES (1, 1, 1), (1, 1, 1), (1, 1, 2), (2, 1, 2), (2, 1, 2),
(2, 1, 1), (3, 1, 1), (4, 1, 1), (4, 1, 1), (5, 1, 1)
) R1 (X, Y, Z)
INTERSECT
SELECT X, Y, Z,
ROW_NUMBER() OVER(PARTITION BY X, Y, Z ORDER BY (SELECT 1)
Guide to Migrating from DB2 to SQL Server and Azure SQL DB Page | 63
) AS TMP$NUM
FROM (VALUES (1, 1, 1), (1, 1, 1), (2, 1, 2), (3, 1, 1), (3, 1, 2),
(3, 2, 1), (3, 1, 3), (4, 2, 1)
) R2 (X, Y, Z)
) R3
ORDER BY X, Y, Z
SELECT X, Y, Z FROM (VALUES (1, 1, 1), (1, 1, 1), (1, 1, 2), (2, 1, 2),
(2, 1, 2), (2, 1, 1), (3, 1, 1), (4, 1, 1), (4, 1, 1), (5, 1, 1)
) R1 (X, Y, Z)
INTERSECT
SELECT X, Y, Z FROM (VALUES (1, 1, 1), (1, 1, 1), (2, 1, 2), (3, 1, 1),
(3, 1, 2), (3, 2, 1), (3, 1, 3), (4, 2, 1)
) R2 (X, Y, Z)
ORDER BY X, Y, Z
In DB2, The ORDER BY clause specifies an ordering of the rows of the result table. The ORDER OF table-
designator clause specifies that the same ordering used in table-designator applies to the result table of the
subselect. The ordering that is applied is the same as if the columns of the ORDER BY clause in the nested
subselect (or fullselect) were included in the outer subselect (or fullselect), and these columns were specified
in place of the ORDER OF clause.
Links: DB2 for Linux UNIX and Windows 10.5.0, DB2 for z/OS 11.0.0, SQL Server 2014.
DB2 Example:
SELECT ID, VAL
FROM (
SELECT ID, VAL
FROM TABLE_FF
ORDER BY ID DESC
) A
ORDER BY VAL, ORDER OF A
Solution:
In SQL Server, the ORDER BY clause is invalid in views, inline functions, derived tables, subqueries, and
common table expressions, unless TOP, OFFSET or FOR XML is also specified. Also functionality of
ORDER OF is not supported in SQL Server.
Replace the ORDER OF clause with a list of fields or aliases from the subquery sort specification. Then either
remove the ORDER BY clause from the subquery (see example 1) or add the TOP clause to the subquery
(see example 2) or add the OFFSET clause to the subquery (see example 3).
Guide to Migrating from DB2 to SQL Server and Azure SQL DB Page | 64
FROM (
SELECT TOP 100 PERCENT ID, VAL
FROM TABLE_FF
ORDER BY ID DESC
) A
ORDER BY VAL, ID DESC
In DB2, when a correlation name is specified, column names can also be specified to give names to the
columns of the table name, view name, nickname, function name reference, or nested table expression.
Links: DB2 for Linux UNIX and Windows 10.5.0, DB2 for z/OS 11.0.0, SQL Server 2014.
DB2 Example 1:
SELECT A, B
FROM (SELECT ID, VAL FROM TABLE_FF) AS C (A,B)
ORDER BY A;
DB2 Example 2:
SELECT A, B
FROM TABLE_C AS C (A,B)
ORDER BY A;
Solution:
In SQL Server, column aliases can be specified only after derived table (like nested table in DB2). Replace
the column aliases with the column names with aliases (see example 2).
In DB2, you do not need to specify an alias for a nested table expression.
Guide to Migrating from DB2 to SQL Server and Azure SQL DB Page | 65
Links: DB2 for Linux UNIX and Windows 10.5.0, DB2 for z/OS 11.0.0, SQL Server 2014.
DB2 Example:
SELECT VAL
FROM (SELECT ID, VAL FROM TABLE_FF)
ORDER BY ID;
Solution:
In SQL Server, you must include an alias for a derived table (subquery). Add an alias for the nested table
expression.
In general in DB2, a table function, together with its argument values, can be referenced in the FROM clause
of a SELECT in exactly the same way as a table or view.
Links: DB2 for Linux UNIX and Windows 10.5.0, DB2 for z/OS 11.0.0, SQL Server 2014.
DB2 Example:
CREATE FUNCTION FUNC_TABLE_LIST
(PAR_SCHEMA VARCHAR(30), PAR_NAME CHAR(1))
RETURNS TABLE (NAME VARCHAR(128), CTIME TIMESTAMP)
RETURN
SELECT NAME, CTIME
FROM SYSIBM.SYSTABLES
WHERE (UPPER(CREATOR) = UPPER(PAR_SCHEMA))
AND (UPPER(SUBSTR(NAME, 1, 1)) = UPPER(PAR_NAME));
SELECT *
FROM TABLE(FUNC_TABLE_LIST('DB2_OBJECTS', 'A')) AS T;
Solution:
In SQL Server, the syntax of a table-valued function call in a FROM clause is different from that in DB2. In
SQL Server, remove the TABLE keyword and the parentheses around the function name in the FROM
clause.
Guide to Migrating from DB2 to SQL Server and Azure SQL DB Page | 66
SELECT *
FROM FUNC_TABLE_LIST('DBO', 'A') AS T;
In DB2, a data-change-table-reference clause specifies an intermediate result table. This table is based on
the rows that are directly changed by the searched UPDATE, searched DELETE, or INSERT statement that
is included in the clause. A data-change-table-reference can be specified as the only table-reference in the
FROM clause of the outer fullselect that is used in a select-statement, a SELECT INTO statement, or a
common table expression.
Links: DB2 for Linux UNIX and Windows 10.5.0, DB2 for z/OS 11.0.0, SQL Server 2014.
In DB2, the table types for the data-change-table-reference are:
OLD: Specifies that the rows of the intermediate result table represent the set of rows that are changed
by the SQL data change statement as they existed before the application of the data change statement.
NEW: Specifies that the rows of the intermediate result table represent the set of rows that are changed
by the SQL data change statement before the application of referential constraints and AFTER triggers.
Data in the target table at the completion of the statement might not match the data in the intermediate
result table because of additional processing for referential constraints and AFTER triggers.
FINAL: Specifies that the rows of the intermediate result table represent the set of rows that are changed
by the SQL data change statement as they exist at the completion of the data change statement.
The content of the intermediate result table for a data-change-table-reference is determined when the
cursor opens. The intermediate result table contains all manipulated rows, including all the columns in the
specified target table or view. All the columns of the target table or view for an SQL data change
statement are accessible using the column names from the target table or view. If an INCLUDE clause
was specified within a data change statement, the intermediate result table will contain these additional
columns.
DB2 Example 1:
SELECT ID, VAL, MODIFY, COMMENT
FROM NEW TABLE (
INSERT INTO TABLE_FF (ID, VAL) INCLUDE (COMMENT VARCHAR(128))
VALUES (1, 'ABCDEFG', 'INSERT_NEW')
);
DB2 Example 2:
SELECT ID, VAL, MODIFY, COMMENT
FROM OLD TABLE (
UPDATE TABLE_FF INCLUDE (COMMENT VARCHAR(128)) SET
VAL = 'ABCDEFG',
MODIFY = CURRENT TIMESTAMP,
COMMENT = 'UPDATE OLD'
WHERE ID = 10
);
Guide to Migrating from DB2 to SQL Server and Azure SQL DB Page | 67
SELECT ID, VAL, MODIFY, COMMENT
FROM NEW TABLE (
UPDATE TABLE_FF INCLUDE (COMMENT VARCHAR(128)) SET
VAL = 'ABCDEFG',
MODIFY = CURRENT TIMESTAMP,
COMMENT = 'UPDATE NEW'
WHERE ID = 10
);
DB2 Example 3:
SELECT ID, VAL, MODIFY, COMMENT
FROM OLD TABLE (
DELETE FROM TABLE_FF INCLUDE (COMMENT VARCHAR(128))
SET COMMENT = 'DELETE OLD'
WHERE ID = 10
);
Solution:
In SQL Server, rewrite the SELECT statement with the data-change-table-reference clause in SQL Server
syntax, using the OUTPUT clause in the nested INSERT, UPDATE, and DELETE statements. For table
types, make the following changes:
To emulate the DB2 OLD table, use the DELETED column prefix.
To emulate the DB2 NEW table, use the INSERTED column prefix.
No possibility to emulate the DB2 FINAL table.
You can emulate the included columns by adding constants or expressions to the output select list.
Guide to Migrating from DB2 to SQL Server and Azure SQL DB Page | 68
SQL Server Example 3:
DELETE FROM TABLE_FF
OUTPUT DELETED.ID, DELETED.VAL, DELETED.MODIFY, 'DELETE OLD' COMMENT
WHERE ID = 10;
In DB2, when you set the DB2_COMPATIBILITY_VECTOR registry variable to support the outer join operator
(+), queries can use this operator as alternative syntax within predicates of the WHERE clauseLinks: DB2 for
Linux UNIX and Windows 10.5.0, SQL Server 2014.
DB2 Example 1:
SELECT * FROM TABLE_FF A, TABLE_F B
WHERE A.ID = B.ID (+);
DB2 Example 2:
SELECT * FROM TABLE_FF A, TABLE_F B
WHERE A.ID (+) = B.ID;
Solution:
In SQL Server, rewrite the joins to ANSI format. If the operator (+) is specified with right operand, the joins
are converted to LEFT OUTER JOIN. If the operator (+) is specified with left operand, the joins are converted
to RIGHT OUTER JOIN.
A hierarchical query is a form of recursive query that retrieves a hierarchy from relational data by using a
CONNECT BY clause. You can then use CONNECT BY syntax, including pseudocolumns, unary operators,
and the SYS_CONNECT_BY_PATH scalar functionLinks: DB2 for Linux UNIX and Windows 10.5.0, SQL
Server 2014.
DB2 Example:
SELECT
NAME,
LEVEL,
SALARY,
CONNECT_BY_ROOT NAME AS ROOT,
SYS_CONNECT_BY_PATH(NAME, ':') AS CHAIN
FROM MY_EMP
START WITH NAME = 'GOYAL'
CONNECT BY PRIOR EMPID = MGRID
Guide to Migrating from DB2 to SQL Server and Azure SQL DB Page | 69
Solution:
In SQL Server, emulate hierarchical queries by using WITH common_table_expression.
UNION ALL
SELECT
E.EMPID,
E.NAME,
D.LEVEL + 1 LEVEL,
E.SALARY,
D.ROOT,
CAST(D.CHAIN + ':' + E.NAME AS VARCHAR(MAX)) CHAIN,
CAST(D.SORT + '.' +
CAST(E.EMPID AS VARCHAR(10)) AS VARCHAR(MAX)) SORT
FROM MY_EMP E
JOIN EMP_REPORT AS D ON E.MGRID = D.EMPID
)
SELECT NAME, LEVEL, SALARY, ROOT, CHAIN
FROM EMP_REPORT
ORDER BY SORT;
In DB2, ROWNUM numbers the records in a result set. The first record that meets the WHERE clause criteria
in a SELECT statement is given a row number of 1, and every subsequent record meeting that same criteria
increases the row number. Note that ROWNUM is affected by the ORDER BY clauseLinks: DB2 for Linux
UNIX and Windows 10.5.0, SQL Server 2014.
DB2 Example:
SELECT T.*
FROM TABLE_FF T
WHERE ROWNUM BETWEEN 2 AND 5
ORDER BY ID DESC
Solution:
In SQL Server, emulate ROWNUM pseudocolumn by using ROW_NUMBER Ranking Function. This function
can only appear in the SELECT or ORDER BY clauses.
3.4.1.12 common-table-expression
A common table expression permits defining a result table with a table-name that can be specified as a table
name in any FROM clause of the fullselect that follows.
Links: DB2 for Linux UNIX and Windows 10.5.0, DB2 for z/OS 11.0.0, SQL Server 2014.
DB2 Example:
WITH PLUS AS (
SELECT NAME, DEPT
FROM DB2_TABLES.EMPLOYEES
WHERE DEPT=20)
SELECT NAME, DEPT FROM PLUS;
Solution:
In MS SQL Server you could use the same WITH common_table_expression – which specifies a temporary
named result set.
DB2 Example:
CREATE TABLE DB2_TABLES.MYEMPLOYEE (EMPLOYEEID INT, MANAGERID INT)
Guide to Migrating from DB2 to SQL Server and Azure SQL DB Page | 71
SELECT E.MANAGERID, E.EMPLOYEEID, EMPLOYEELEVEL + 1
FROM DB2_TABLES.MYEMPLOYEE AS E,DIRECTREPORTS AS D
WHERE E.MANAGERID = D.EMPLOYEEID
)
SELECT MANAGERID, EMPLOYEEID, EMPLOYEELEVEL
FROM DIRECTREPORTS
ORDER BY MANAGERID;
Solution:
In MS SQL Server you could use the same WITH common_table_expression – which specifies a temporary
named result set.
DB2 Example:
WITH PLUS (AA,BB) AS (
SELECT NAME, DEPT
FROM DB2_TABLES.EMPLOYEES
WHERE DEPT=20)
SELECT AA, BB FROM PLUS;
Solution:
Guide to Migrating from DB2 to SQL Server and Azure SQL DB Page | 72
In SQL Server, the VALUES statement is not supported. For emulation use the table value constructor to
specify multiple values in the FROM clause of a SELECT statement. The values list must be always enclosed
in parentheses. Aliases for sets and columns must be specified.
In DB2, you can write single values in a VALUES clause with or without parentheses. In SQL Server, the
values list must be always enclosed in parentheses.
Links: DB2 for Linux UNIX and Windows 10.5.0, DB2 for z/OS 11.0.0, SQL Server 2014.
DB2 Example:
INSERT INTO TABLE_INS (ID) VALUES 1;
INSERT INTO TABLE_INS (ID) VALUES 1,(2),3;
Solution:
In SQL Server, add parentheses to all single-row values.
In DB2, you can use a subquery (fullselect, insertable view) as the object of the INSERT operation.
Links: DB2 for Linux UNIX and Windows 10.5.0, DB2 for z/OS 11.0.0, SQL Server 2014.
DB2 Example:
INSERT INTO (SELECT * FROM TABLE_INS) VALUES (1,'ABC');
Solution:
In SQL Server, use the common table expression (CTE) to emulate this functionality.
Guide to Migrating from DB2 to SQL Server and Azure SQL DB Page | 73
3.4.3.3 Common Table Expression (CTE) in INSERT Statement
DB2 and SQL Server have different syntaxes for the common table expression (CTE) in an INSERT statement.
In DB2, a CTE can be used only with the SELECT part of an INSERT SELECT statement. In SQL Server, you
can specify the CTE within the scope of the INSERT statement.
Links: DB2 for Linux UNIX and Windows 10.5.0, DB2 for z/OS 11.0.0, SQL Server 2014.
DB2 Example:
INSERT INTO TABLE_INS (ID, VAL)
WITH PLUS AS (
SELECT TD.ID, TD.VAL, TI.ID AS NEW
FROM TABLE_DEL TD LEFT OUTER JOIN TABLE_INS TI ON TD.ID=TI.ID
)
SELECT ID, VAL FROM PLUS WHERE NEW IS NULL;
Solution:
In SQL Server, place the CTE before the INSERT statement.
When you create a structured type, DB2 automatically generates a constructor function for the
type, and generates mutator and observer methods for the attributes of the type. You can use these
methods to create instances of structured types, and insert these instances into a column of a table.
When you create a structured type, DB2 creates a function of the same name as the type is created.
This function has no parameters and returns an instance of the type with all of its attributes set to null.
The function that is created for structured type DB2_OBJECTS.MAN_T, for example, has the following
format:
DB2 Example:
CREATE FUNCTION MAN_T() RETURNS MAN_T
To construct an instance of a type to insert into a column, use the constructor function with the mutator
methods. A mutator method exists for each attribute of an object. So, for type MAN_T, DB2 for Linux, UNIX,
and Windows creates mutator methods for each of the following attributes: (Name, Age, DOB).
The mutator method DB2 creates for attribute Age, for example, has the following format:
DB2 Example:
ALTER TYPE MAN_T ADD METHOD AGE(INT) RETURNS MAN_T
An observer method exists for each attribute of an object. If the method for an attribute receives an
object of the expected type or subtype, the method returns the value of the attribute for that object.
Guide to Migrating from DB2 to SQL Server and Azure SQL DB Page | 74
The observer method DB2 creates for the attribute Age of the type MAN_T, for example, has the
following format:
DB2 Example:
Assume that you want to add a new row to the typed table, and that you want that row to contain person
information (PERSON_T). Just as with built-in data types, you can add this row using INSERT with the VALUES
clause. However, when you specify the value to insert into the PERSON_T, you must invoke the system-
provided constructor function (DB2_OBJECTS.PERSON_T() ) and observer methods (..Name()..Age ()..DOB)
to create the values. To invoke a method on a structured type, use the method invocation operator: '..'.
DB2 Example:
CREATE TABLE DB2_OBJECTS.WOMAN (EYES_COLOUR VARCHAR (20), HAIR_COLOUR
VARCHAR(20), PERSON DB2_OBJECTS.PERSON_T)
/
INSERT INTO DB2_OBJECTS.WOMAN (EYES_COLOUR, HAIR_COLOUR, PERSON)
VALUES('GREEN', 'RED',
DB2_OBJECTS.PERSON_T()
..NAME ('ANGELINA')
..AGE (26)
..DOB (DATE('25.12.1988')));
/
SELECT EYES_COLOUR, HAIR_COLOUR, PERSON..NAME, PERSON..AGE, PERSON..DOB
FROM DB2_OBJECTS.WOMAN
To avoid having to explicitly call the mutator methods for each attribute of a structured type every time
you create an instance of the type, consider defining your own SQL-bodied constructor function that initializes
all of the attributes. The following example contains the declaration for an SQL-bodied constructor function for
the US_addr_t type:
There are two common approaches of generating unique values, both of which can be applied to object
identifiers:
with SEQUENCEs
with the GENERATE_UNIQUE function
Links: DB2 for Linux UNIX and Windows 10.5.0, SQL Server 2014.
If you need to use numeric values as object identifiers, you can use a SEQUENCE. To begin, use the
REF USING clause to specify that the base type of the object reference is to be a numeric type, in the following
case, an INT:
DB2 Example:
CREATE TYPE DB2_OBJECTS.MAN_T AS (NAME VARCHAR(20), AGE INT, DOB DATE)
Guide to Migrating from DB2 to SQL Server and Azure SQL DB Page | 75
REF USING INT MODE DB2SQL;
/
CREATE TABLE DB2_OBJECTS.MAN OF DB2_OBJECTS.MAN_T (REF IS OID USER
GENERATED)
/
CREATE SEQUENCE DB2_OBJECTS.MANOID AS REF(DB2_OBJECTS.MAN_T)
START WITH 1 INCREMENT BY 1
/
INSERT INTO DB2_OBJECTS.MAN (OID, NAME, AGE, DOB)
VALUES (NEXT VALUE FOR DB2_OBJECTS.MANOID,'ALEX', 26, DATE('25.12.1988'))
As an alternative to using SEQUENCEs to generate object identifiers, you can use the
GENERATE_UNIQUE function. Because GENERATE_UNIQUE returns a CHAR (13) FOR BIT DATA value,
ensure that the REF USING clause on the CREATE TYPE statement can accommodate a value of that type.
The default of VARCHAR (16) FOR BIT DATA is suitable for this purpose.
DB2 Example:
INSERT INTO DB2_OBJECTS.WOMEN (OID, NAME, AGE, DOB)
VALUES(DB2_OBJECTS.WOMAN_T (GENERATE_UNIQUE ()), 'ALEX', 26,
DATE('25.12.1988'));
/
DB2 Example:
INSERT INTO DB2_OBJECTS.WOMEN (OID, NAME, AGE, DOB)
VALUES(DB2_OBJECTS.WOMAN_T('A'), 'ALEX', 26, DATE('25.12.1988'));
In DB2, you can use a subquery (fullselect, updatable view) as the object of the UPDATE operation.
Links:
DB2 for Linux UNIX and Windows 10.5.0, DB2 for z/OS 11.0.0, SQL Server 2014
DB2 Example:
UPDATE (SELECT * FROM A)
SET
B = B||C
WHERE A=3
Solution:
In SQL Server, use the CTE to emulate this functionality by moving the updatable subquery to the CTE.
In DB2, you can update a few columns from a single subquery result.
Links: DB2 for Linux UNIX and Windows 10.5.0, DB2 for z/OS 11.0.0, SQL Server 2014
DB2 Example:
UPDATE
EMPLOYEES E
SET
(ID, LAST_NAME) = (SELECT ID, PNAME FROM EMP_PATRONYMIC P WHERE
P.ID=E.ID),
(NAME, SAL) = (NAME||' '||LAST_NAME, SAL/10);
Solution:
In SQL Server, remove from all subqueries the conditions that reference the up-level objects, and move the
subqueries from the SET clause to the CTE.
Generate a single assignment for each column from the column group. Use the expression from the
appropriate CTE for the assignment.
To generate join conditions, use the conditions that were removed from the original subqueries.
SQL Server doesn’t support multiple uses of each clause in a MERGE statement..
Links: DB2 for Linux UNIX and Windows 10.5.0, DB2 for z/OS 11.0.0, SQL Server 2014
DB2 Example:
MERGE INTO DB2_DML_PROC.MERG_IN_EMP E
USING DB2_DML_PROC.USING_EMP E1
ON (E.ID = E1.ID)
WHEN MATCHED AND E.ID < 4 THEN
UPDATE SET
E.SAL = E1.SAL
WHEN MATCHED AND E.ID > 4 THEN
UPDATE SET
E.SAL = 111
WHEN MATCHED AND E.ID = 4 THEN
UPDATE SET
E.SAL = 444
WHEN NOT MATCHED THEN
INSERT VALUES(E1.ID, E1.NAME, E1.LAST_NAME, E1.SAL);
Solution:
In SQL Server, you can use the CASE statement to emulate multiple uses. You can use the condition (1=1)
to emulate the Searched WHEN clause.
Note: In the CASE statement you can’t use the DEFAULT value. You must write the default value
manually.
Guide to Migrating from DB2 to SQL Server and Azure SQL DB Page | 78
END
WHEN NOT MATCHED THEN
INSERT VALUES(E1.ID, E1.NAME, E1.LAST_NAME, E1.DOB, E1.DEPT, E1.SAL,
E1.JOB);
In DB2, you can use the SIGNAL exception to make the MERGE statement fail.
Links: DB2 for Linux UNIX and Windows 10.5.0, DB2 for z/OS 11.0.0, SQL Server 2014
DB2 Example:
MERGE INTO DB2_DML_PROC.MERG_IN_EMP E
USING DB2_DML_PROC.USING_EMP E1
ON (E.ID = E1.ID)
WHEN MATCHED AND E.ID < 4 THEN
UPDATE SET
E.SAL = E1.SAL
WHEN MATCHED AND E.ID > 4 THEN
UPDATE SET
E.SAL = 111
WHEN MATCHED AND E.ID = 4 THEN
SIGNAL SQLSTATE '70102' SET MESSAGE_TEXT = 'ERROR SALARY'
WHEN NOT MATCHED THEN
INSERT VALUES(E1.ID, E1.NAME, E1.LAST_NAME, E1.SAL);
Solution:
In SQL Server, you can generate a conversion exception in UPDATE or INSERT, so that the MERGE
statement will fail.
In DB2, a MERGE statement can have mixed UPDATE and DELETE clauses with intersects conditions. This
cannot be emulated in SQL Server automaticaly. It requires manual emulation.
Links: DB2 for Linux UNIX and Windows 10.5.0, DB2 for z/OS 11.0.0, SQL Server 2014
Guide to Migrating from DB2 to SQL Server and Azure SQL DB Page | 79
DB2 Example:
MERGE INTO DB2_DML_PROC.MERGE_INTO_TAB D
USING (SELECT ID, VAL FROM DB2_DML_PROC.MERGE_INTO_TAB UNION VALUES (4,
'D') UNION VALUES (5, 'E') UNION VALUES (6, 'F')) AS B (ID, VAL)
ON (D.ID=B.ID)
WHEN MATCHED AND D.ID=1 THEN
UPDATE SET VAL='X'
WHEN MATCHED AND D.ID<3 THEN
DELETE
WHEN MATCHED THEN
UPDATE SET VAL='Y'
WHEN NOT MATCHED THEN
INSERT VALUES (B.ID,B.VAL);
Solution:
SQL Server does not have similar functionality; there is no migration solution for this DB2 capability yet.
DB2 Example:
MERGE INTO (SELECT * FROM DB2_DML_PROC.MERG_IN_EMP) E
USING DB2_DML_PROC.USING_EMP E1
ON (E.ID = E1.ID)
WHEN MATCHED AND E.ID < 4 THEN
UPDATE SET
E.SAL = E1.SAL
WHEN NOT MATCHED THEN
INSERT VALUES(E1.ID, E1.NAME, E1.LAST_NAME, E1.SAL);
Solution:
In SQL Server, you can use a common table expression (CTE) to emulate this functionality.
Guide to Migrating from DB2 to SQL Server and Azure SQL DB Page | 80
3.4.5.5 Column Groups in an UPDATE SET Clause
In DB2, in a MERGE statement you can use the UPDATE SET clause to update several columns from a
single subquery result.
Links: DB2 for Linux UNIX and Windows 10.5.0, DB2 for z/OS 11.0.0, SQL Server 2014
DB2 Example:
MERGE INTO DB2_DML_PROC.MERG_IN_EMP E
USING DB2_DML_PROC.USING_EMP E1
ON (E.ID = E1.ID)
WHEN MATCHED AND E.ID < 4 THEN
UPDATE SET
(E.NAME, E.SAL) = (SELECT 'DBBEST - '||NAME, SAL*2 FROM
DB2_DML_PROC.MERG_IN_EMP EM WHERE EM.ID = E.ID)
WHEN NOT MATCHED THEN
INSERT VALUES(E1.ID, E1.NAME, E1.LAST_NAME, E1.SAL);
Solution:
In SQL Server, you can generate a single assignment and a duplicate subquery for each column from a
column group.
DB2 Example:
MERGE INTO DB2_DML_PROC.MERGE_INTO_TAB MI
USING (SELECT ID, VAL FROM DB2_DML_PROC.USING_TAB) US
ON (MI.ID = US.ID)
WHEN NOT MATCHED AND US.ID = 3 THEN
INSERT (MI.ID, MI.VAL)
VALUES (US.ID, US.VAL)
WHEN NOT MATCHED AND US.ID = 4 THEN
INSERT (MI.ID, MI.VAL)
VALUES (5, 'BLA-BLA');
DB2 Example:
MERGE INTO DB2_DML_PROC.MERG_IN_EMP E
USING DB2_DML_PROC.USING_EMP E1
ON (E.ID = E1.ID)
WHEN MATCHED AND E.ID < 4 THEN
UPDATE SET E.SAL = 77777
WHEN MATCHED AND E.ID = 4 THEN
UPDATE SET E.SAL = 11111
WHEN MATCHED THEN
UPDATE SET E.SAL = 22222
WHEN NOT MATCHED THEN
INSERT VALUES(E1.ID, E1.NAME, E1.LAST_NAME, E1.SAL);
DB2 Example:
MERGE INTO DB2_DML_PROC.MERG_IN_EMP E
USING DB2_DML_PROC.USING_EMP E1
ON (E.ID = E1.ID)
WHEN MATCHED AND E.ID < 4 THEN
UPDATE SET E.SAL = 77777
WHEN MATCHED AND E.ID = 4 THEN
UPDATE SET E.SAL = 22222
WHEN MATCHED AND E.ID > 4 THEN
DELETE
WHEN NOT MATCHED THEN
INSERT VALUES(E1.ID, E1.NAME, E1.LAST_NAME, E1.SAL);
DB2 Example:
MERGE INTO DB2_DML_PROC.MERGE_INTO_TAB D
USING DB2_DML_PROC.USING_TAB AS B
ON (D.ID=B.ID)
WHEN MATCHED AND D.ID=1 THEN
Guide to Migrating from DB2 to SQL Server and Azure SQL DB Page | 82
DELETE
WHEN MATCHED THEN
DELETE
WHEN MATCHED AND D.ID=2 THEN
UPDATE SET VAL='Y'
WHEN NOT MATCHED THEN
INSERT VALUES (B.ID,B.VAL);
In DB2, a correlation clause can be used in a DELETE statement to designate a table, view, nickname,
fullselect, or column names.
Links: DB2 for Linux UNIX and Windows 10.5.0, DB2 for z/OS 11.0.0, SQL Server 2014.
DB2 Example:
DELETE FROM TABLE_DEL AS D (A,B,C,D) WHERE D.A BETWEEN 100 AND 109;
Solution:
In SQL Server, replace the column aliases with the column names in a search condition, and remove the
correlation clause from the DELETE statement.
In DB2, the syntax of using a subquery as the object of the DELETE operation is different from that in
SQL Server.
Links: DB2 for Linux UNIX and Windows 10.5.0, DB2 for z/OS 11.0.0, SQL Server 2014.
DB2 Example:
-- DELETING DUPLICATES
DELETE FROM
(SELECT ROWNUMBER() OVER (PARTITION BY ID ORDER BY IDENT)
FROM DB2_OBJECTS.IDENT) AS E (RN)
WHERE RN > 1
Solution:
Guide to Migrating from DB2 to SQL Server and Azure SQL DB Page | 83
In SQL Server, convert such queries by assigning an alias to the subquery and then adding a FROM clause
with this alias.
Regardless of the isolation level, the database manager places exclusive locks on every row that is inserted,
updated, or deleted. Thus, all isolation levels ensure that any row that is changed by an application process
during a unit of work is not changed by any other application process until the unit of work is complete.
DB2 Example 1:
SELECT ID, VAL
FROM TABLE_FF
WHERE ID BETWEEN 65 AND 90
WITH UR
DB2 Example 2:
Guide to Migrating from DB2 to SQL Server and Azure SQL DB Page | 84
SELECT ID, VAL
FROM TABLE_FF
WHERE ID BETWEEN 65 AND 90
WITH RS USE AND KEEP EXCLUSIVE LOCKS
Solution:
In SQL Server, use table hints to set the isolation level and lock type. Correspondence between the DB2 and
SQL Server isolation levels and lock types is presented in Table 2.
Table 2: Isolation Level and Lock Type Differences Between DB2 and SQL Server
DB2 Isolation Level Description SQL Server Table Hint
RR Repeatable Read SERIALIZABLE
RS Read Stability REPEATABLEREAD
CS Cursor Stability READCOMMITTED
UR Uncommitted Read READUNCOMMITTED
DB2 Lock Type Description SQL Server Table Hint
SHARE Concurrent processes can acquire SHARE or TABLOCK
UPDATE locks on the data.
UPDATE Concurrent processes can acquire SHARE UPDLOCK
locks on the data, but no concurrent process
can acquire an UPDATE or EXCLUSIVE lock.
EXCLUSIVE Concurrent processes cannot acquire a lock TABLOCKX
on the data.
Unlike DB2, SQL Server does not lock the current cursor row in READCOMMITTED isolation level.
3.5 Routines
This section describes migration issues for DB2 stored procedures and user-defined functions.
The syntax of DB2’s routines language is significantly different from the syntax of SQL Server’s procedural
language, Transact-SQL. This makes converting code from stored procedures, functions, or triggers a
challenge. SSMA, however, can resolve most of the problems related to these conversions.
3.5.1 Procedures
3.5.1.1 Overloaded Procedures
In DB2, two procedures can exist in one schema with the same name but different parameter types or a
different number of parameters. SQL Server 2014 – SQL Server does not support this.
Guide to Migrating from DB2 to SQL Server and Azure SQL DB Page | 85
Links: DB2 for Linux UNIX and Windows 10.5.0, DB2 for z/OS 11.0.0 – DB2 zOS does not support
overloaded procedures
DB2 Example 1:
-- PROCEDURE
CREATE PROCEDURE PROC_NAME (
IN A INTEGER,
IN B INTEGER
)
LANGUAGE SQL
BEGIN
SQL STATEMENT
…
END;
--OVERLOAD PROCEDURE
CREATE PROCEDURE PROC_NAME ( -- NAME OF PROCEDURE IS LIKE IN THE STATEMENT
ABOVE
IN A INTEGER,
IN B INTEGER,
IN C INTEGER
)
LANGUAGE SQL
BEGIN
SQL STATEMENT
…
END;
Solution:
In SQL Server, you must choose distinct names for procedures that have the same DB2 name but different
parameter signatures.
--PROCEDURE2
CREATE PROCEDURE PROC_NAME$OVL2 –- WE ADD SUFFIX $OVL2 IN A NAME OF THE
SECOND PROCEDURE
@A INT,
@B INT,
@C INT
AS
BEGIN
SQL STATEMENT
…
END;
Guide to Migrating from DB2 to SQL Server and Azure SQL DB Page | 86
3.5.1.2 OUT and INOUT Parameters
In DB2, if a procedure has an OUT parameter, it always returns a value in a call variable. In SQL Server this
construct cannot return any value SQL Server 2014.
Links: DB2 for Linux UNIX and Windows 10.5.0, DB2 for z/OS 11.0.0
If argument has an IN then its value can’t change into a body of procedure. If argument has an OUT or an IN
OUT then its value can change into body. Also, we can’t set a constant value for OUT or (IN OUT)-arguments
in external call. For IN-arguments it's should be variable. Using of OUT-argument cannot send value into a
body of procedure. Need use IN or IN OUT for this.
DB2 Example:
CREATE PROCEDURE PROC_IN_OUT(
IN P_IN1 INTEGER DEFAULT 6,
INOUT P_INOUT INTEGER,
OUT P_OUT INTEGER
)
LANGUAGE SQL
BEGIN
SET P_OUT = P_IN1 * P_INOUT;
SET P_INOUT = P_IN1 + P_INOUT;
END;
BEGIN ATOMIC
DECLARE RET1 INTEGER;
DECLARE RET2 INTEGER;
SET RET2 = 4;
CALL PROC_IN_OUT(5, RET2, RET1);
CALL DBMS_OUTPUT.ENABLE(5000);
CALL DBMS_OUTPUT.PUT_LINE('RET1 = '||RET1||'; RET2 = '||RET2);
END
SELECT DB2ADMIN.WRAPPER_DBMS_OUTPUT() A FROM SYSIBM.SYSDUMMY1;
BEGIN ATOMIC
DECLARE RET1 INTEGER;
DECLARE RET2 INTEGER;
SET RET2 = 4;
CALL PROC_IN_OUT(DEFAULT, RET2, RET1);
CALL DBMS_OUTPUT.ENABLE(5000);
CALL DBMS_OUTPUT.PUT_LINE('RET1 = '||RET1||'; RET2 = '||RET2);
END
SELECT DB2ADMIN.WRAPPER_DBMS_OUTPUT() A FROM SYSIBM.SYSDUMMY1;
BEGIN ATOMIC
DECLARE RET1 INTEGER;
DECLARE RET2 INTEGER;
SET RET2 = 4;
CALL PROC_IN_OUT( P_INOUT=>RET2, P_OUT=>RET1);
CALL DBMS_OUTPUT.ENABLE(5000);
CALL DBMS_OUTPUT.PUT_LINE('RET1 = '||RET1||'; RET2 = '||RET2);
END
SELECT DB2ADMIN.WRAPPER_DBMS_OUTPUT() A FROM SYSIBM.SYSDUMMY1;
Guide to Migrating from DB2 to SQL Server and Azure SQL DB Page | 87
Solution:
In SQL Server, when you call a procedure using EXEC for each OUT variable, you must add the OUTPUT
keyword.
In DB2, two functions can exist in one schema with the same name but different parameter types or a
different number of parameters. SQL Server 2014 – SQL Server does not support this.
Links: DB2 for Linux UNIX and Windows 10.5.0, DB2 for z/OS 11.0.0 – DB2 zOS does not support
overloaded procedures
DB2 Example 1:
CREATE FUNCTION FUNC1 (
IN P1 INTEGER
)RETURNS INTEGER
LANGUAGE SQL
BEGIN
RETURN P1*P1;
END;
Guide to Migrating from DB2 to SQL Server and Azure SQL DB Page | 88
IN P1 VARCHAR(100)
)RETURNS INTEGER
LANGUAGE SQL
BEGIN
RETURN LENGTH(P1);
END;
Solution:
In SQL Server, you must choose distinct names for the functions that have the same DB2 name but different
parameter signatures.
CREATE FUNCTION FUNC1$OVL1( --WE ADD SUFFIX $OVL1 IN A NAME OF THE FIRST
PROCEDURE
@P1 INT
)RETURNS INTEGER AS
BEGIN
RETURN @P1*@P1;
END;
CREATE FUNCTION FUNC1$OVL2( --WE ADD SUFFIX $OVL2 IN A NAME OF THE FIRST
PROCEDURE
@P1 VARCHAR(100)
)RETURNS INTEGER AS
BEGIN
RETURN LEN(@P1);
END;
In DB2, the RETURNS ROW function specifies that the output of the function is a single row. If the function
returns more than one row, an error is raised (SQLSTATE 21505).
Solution:
In SQL Server, create a table-valued function and make sure that the SELECT statement returns one row.
See RETURN Statement in Table Functions or Procedures later in this section for more details.
In DB2, the MODIFIES SQL DATA clause is used when a function can change data in a table (using Data
Manipulation Language, or DML), either directly or by a call procedure.
Solution:
In SQL Server, user-defined functions (UDFs) can’t use DML statements or executing procedures. You can
emulate the MODIFIES SQL DATA clause by using an extended stored procedure.
Guide to Migrating from DB2 to SQL Server and Azure SQL DB Page | 89
3.5.3 Flow Control Constructs
This section covers differences in behavior between DB2 and SQL Server versions of various commands
used in routines that control the flow of execution.
In DB2, the CALL statement can call a procedure or a foreign procedure (but it cannot call a function).
Links: DB2 for Linux UNIX and Windows 10.5.0, DB2 for z/OS 11.0.0, SQL Server 2014.
DB2 Example:
CREATE PROCEDURE P_EX_CALL ( IN A INTEGER, OUT B INTEGER)
BEGIN
SET B = A * 2;
END
BEGIN ATOMIC
DECLARE B INTEGER;
CALL P_EX_CALL(5, B);
SET B = NULL;
CALL P_EX_CALL(NULL, B);
END
Solution:
In SQL Server, use the EXECUTE (EXEC) statement to call a stored procedure.
DECLARE @B INTEGER;
EXECUTE P_EX_CALL 5, @B OUT;
SET @B = NULL;
EXEC P_EX_CALL NULL, @B OUT;
GO
The CASE statement selects an execution path based on multiple conditions. This statement should not be
confused with the CASE expression, which allows an expression to be selected based on the evaluation of
one or more conditions.
Links: DB2 for Linux UNIX and Windows 10.5.0, DB2 for z/OS 11.0.0, SQL Server 2014.
3.5.3.2.1 The simple-case-statement-when-clause
In DB2, the value of the simple-case-statement-when-clause expression prior to the first WHEN keyword is
tested for equality with the value of each expression that follows the WHEN keyword. If the search condition
is true, the THEN statement is executed. If the result is unknown or false, processing continues to the next
Guide to Migrating from DB2 to SQL Server and Azure SQL DB Page | 90
search condition. If the result does not match any of the search conditions, and an ELSE clause is present,
the statements in the ELSE clause are processed.
DB2 Example:
CREATE PROCEDURE DB2_FLOW_CONTR_PROC.P_CASE_EX (IN A INTEGER)
BEGIN
CASE A
WHEN 5 THEN
UPDATE DB2_FLOW_CONTR_PROC.CASE_EX_TAB SET AT3 = A * 3.14;
UPDATE DB2_FLOW_CONTR_PROC.CASE_EX_TAB SET AT1 = AT3;
WHEN 10 THEN
UPDATE DB2_FLOW_CONTR_PROC.CASE_EX_TAB SET AT1 = A / 3.14;
ELSE
UPDATE DB2_FLOW_CONTR_PROC.CASE_EX_TAB SET AT1 = A + ID * 3.14;
UPDATE DB2_FLOW_CONTR_PROC.CASE_EX_TAB SET AT3 = A - ID * 3.14;
END CASE;
END;
Solution:
In SQL Server, because the CASE statement cannot be used in routines as control flow statements, use the
IF....ELSE statement to convert the DB2 CASE statement. In SQL Server the IF or ELSE condition can affect
the performance of only one Transact-SQL statement. To define a statement block, use the control-of-flow
keywords BEGIN and END.
EXECUTE P_CASE_EX1 7;
GO
Guide to Migrating from DB2 to SQL Server and Azure SQL DB Page | 91
DB2 Example:
CREATE PROCEDURE DB2_FLOW_CONTR_PROC.P_CASE_EX_WHEN (IN A INTEGER)
BEGIN
CASE
WHEN A = 5 THEN
UPDATE DB2_FLOW_CONTR_PROC.CASE_EX_TAB SET AT3 = A * 3.14;
UPDATE DB2_FLOW_CONTR_PROC.CASE_EX_TAB SET AT1 = AT3;
WHEN A = 10 THEN
UPDATE DB2_FLOW_CONTR_PROC.CASE_EX_TAB SET AT1 = A / 3.14;
ELSE
UPDATE DB2_FLOW_CONTR_PROC.CASE_EX_TAB SET AT1 = A + ID * 3.14;
UPDATE DB2_FLOW_CONTR_PROC.CASE_EX_TAB SET AT3 = A - ID * 3.14;
END CASE;
END;
CALL DB2_FLOW_CONTR_PROC.P_CASE_EX_WHEN(7);
Solution:
In SQL Server, the solution for the searched-case-statement is exactly the same as for the simple-case-
statement: use the IF....ELSE statement.
EXECUTE P_CASE_EX2 7;
GO
In DB2, the FOR statement executes a statement or group of statements for each row of a table using a
cursor. The cursor can be declared explicitly or implicitly.
Links: DB2 for Linux UNIX and Windows 10.5.0, DB2 for z/OS 11.0.0, SQL Server 2014.
DB2 Example:
CREATE PROCEDURE DB2_FLOW_CONTR_PROC.FOR_EX
BEGIN
Guide to Migrating from DB2 to SQL Server and Azure SQL DB Page | 92
DECLARE NEW_AT VARCHAR(100);
FOR VL AS
SELECT ID AS CURID, AT1 AS CURAT1, AT3 AS CURAT3
FROM DB2_FLOW_CONTR_PROC.CASE_EX_TAB
WHERE ID BETWEEN 1 AND 3
DO
SET NEW_AT = CAST((CURID + CURAT1 - CURAT3) AS VARCHAR(100));
UPDATE DB2_FLOW_CONTR_PROC.CASE_EX_TAB SET AT2 = NEW_AT
WHERE ID = CURID;
END FOR;
END;
CALL DB2_FLOW_CONTR_PROC.FOR_EX;
Solution:
In SQL Server, you can organize the LOOP statement with the WHILE statement, and then use the
@@FETCH_STATUS function to define the end of the loop. The cursor can be declared explicitly only.
OPEN V1;
WHILE @@FETCH_STATUS = 0
BEGIN
CLOSE V1;
DEALLOCATE V1;
END;
GO
EXECUTE FOR_EX;
Guide to Migrating from DB2 to SQL Server and Azure SQL DB Page | 93
3.5.3.5 GET DIAGNOSTICS Statement
In DB2, the GET DIAGNOSTICS statement is used to obtain current execution environment information
including information about the previous SQL statement (other than a GET DIAGNOSTICS statement) that
was executed.
Links: DB2 for Linux UNIX and Windows 10.5.0, DB2 for z/OS 11.0.0.
In DB2, the ROW_COUNT variable identifies the number of rows associated with the previous SQL
statement. If the previous SQL statement is a DELETE, INSERT, or UPDATE statement, ROW_COUNT
identifies the number of rows that qualified for the operation. If the previous statement is a PREPARE
statement, ROW_COUNT identifies the estimated number of result rows in the prepared statement.
DB2 Example:
CREATE PROCEDURE DB2_FLOW_CONTR_PROC.DIAGNOSTIC_ROW_CNT (IN V_ID INTEGER,
OUT ROW_C INTEGER)
BEGIN
UPDATE DB2_FLOW_CONTR_PROC.CASE_EX_TAB SET AT2 = 'DEFINE'
WHERE ID = V_ID;
GET DIAGNOSTICS ROW_C = ROW_COUNT;
END;
BEGIN ATOMIC
DECLARE ROW_C INTEGER;
CALL DB2_FLOW_CONTR_PROC.DIAGNOSTIC_ROW_CNT(60, ROW_C);
END
Solution:
In SQL Server, you can use the @@ROWCOUNT function to return the number of rows that are associated
with the previous SQL statement. However, there is not yet a solution for emulating the GET DIAGNOSTICS
statement when the previous statement is a PREPARE statement.
In DB2, the DB2_RETURN_STATUS identifies the status value returned from the procedure associated with
the previously executed SQL statement, provided that the statement was a CALL statement invoking a
procedure that returns a status. If the previous statement is not such a statement, then the value returned has
no meaning and could be any integer.
Guide to Migrating from DB2 to SQL Server and Azure SQL DB Page | 94
DB2 Example:
CREATE PROCEDURE RETURN_VALUE_EX
BEGIN
RETURN;
END
BEGIN ATOMIC
CALL RETURN_STATUS_EX;
END
Solution:
A SQL Server procedure can return any integer value, and DB2_RETURN_STATUS can be emulated by
returning an integer value from the SQL Server procedure (use EXECUTE and RETURN statements).
EXEC RETURN_STATUS_EX;
GO
3.5.3.8 condition-information
In DB2, condition-information specifies that the error or warning information for the previously executed SQL
statement is to be returned. If information about an error is needed, the GET DIAGNOSTICS statement must
be the first statement specified in the handler that will handle the error. If information about a warning is
Guide to Migrating from DB2 to SQL Server and Azure SQL DB Page | 95
needed, and if the handler will get control of the warning condition, the GET DIAGNOSTICS statement must
be the first statement specified in that handler.
DB2 Example:
CREATE PROCEDURE RETURN_MESSAGE_EX(IN V_ID INTEGER,OUT RETSTR VARCHAR(70))
BEGIN
DECLARE A INTEGER;
BEGIN ATOMIC
DECLARE RETSTR VARCHAR(70) DEFAULT 'OK!';
CALL RETURN_MESSAGE_EX(60, RETSTR);
END
Solution:
In SQL Server, you can use the TRY…CATCH statement and ERROR_MESSAGE ( ) function to set an
output parameter value.
3.5.3.9 IF Statement
In DB2, the IF statement selects an execution path based on the evaluation of a condition.
Links: DB2 for Linux UNIX and Windows 10.5.0, DB2 for z/OS 11.0.0, SQL Server 2014.
DB2 Example:
CREATE PROCEDURE IF_EX(IN V CHAR(1), INOUT STAT INTEGER)
BEGIN
IF V IS NOT NULL THEN
IF V = 'W' THEN
Guide to Migrating from DB2 to SQL Server and Azure SQL DB Page | 96
CALL WHILE_EX;
ELSEIF V = 'R' THEN
CALL REPEAT_EX;
ELSE
CALL ITERATE_EX;
END IF;
ELSE
SET STAT = 1;
END IF;
RETURN STAT;
END
BEGIN ATOMIC
DECLARE STAT INTEGER DEFAULT 0;
CALL IF_EX ('W', STAT);
END
Solution:
In SQL Server, the IF statement differs only in syntax and can be easily converted from DB2.
In DB2, the ITERATE statement causes the flow of control to return to the beginning of a labeled loop.
Links: DB2 for Linux UNIX and Windows 10.5.0, DB2 for z/OS 11.0.0, SQL Server 2014.
DB2 Example 1:
CREATE PROCEDURE ITERATE_EX
BEGIN
DECLARE V_ID INTEGER;
DECLARE CUR1 CURSOR FOR
SELECT ID FROM TAB1 ORDER BY ID;
OPEN CUR1;
Guide to Migrating from DB2 to SQL Server and Azure SQL DB Page | 97
FETCH_LOOP:
BEGIN ATOMIC
CALL ITERATE_EX;
END
Solution 1:
In SQL Server, the CONTINUE statement has similar functionality to the ITERATE statement in DB2, if the
labeled loop is most inner loop enclosing the ITERATE statement, so use it instead of ITERATE. You do not
need to use a label—using just the CONTINUE statement is sufficient.
BEGIN
IF @@FETCH_STATUS <> 0
BREAK;
EXECUTE ITERATE_EX;
GO
DB2 Example 2:
An ITERATE statement can be issued from a nested block to cause that flow of control to return to the
beginning of a loop at a higher level. In the following example, the ITERATE statement within the LAB2
compound statement causes the flow of control to return to the beginning of the LAB1 LOOP statement:
Solution 2:
In SQL Server, we need to mark the end of the labeled loop with a constructed label (adding suffix _continue
sounds as a reasonable choice) and use GOTO instead of ITERATE. If loop conversion creates emulated
increment block, then this block should follow after the target label.
BEGIN
SET A = 0
LAB2:
WHILE 1 = 1
BEGIN
...
LAB3:
WHILE 1 = 1
BEGIN
...
GOTO LAB1_CONTINUE -- MULTILEVEL CONTINUE EMULATED BY GOTO
...
END
...
GOTO LAB1_CONTINUE -- MULTILEVEL CONTINUE EMULATED BY GOTO
...
END
In DB2, the LEAVE statement transfers program control out of a loop or a compound statement.
Links: DB2 for Linux UNIX and Windows 10.5.0, DB2 for z/OS 11.0.0, SQL Server 2014.
DB2 Example 1:
CREATE PROCEDURE LEAVE_EX
BEGIN
DECLARE V_ID INTEGER;
DECLARE CUR1 CURSOR FOR
SELECT ID FROM TAB1 ORDER BY ID;
OPEN CUR1;
FETCH_LOOP:
LOOP
FETCH CUR1 INTO V_ID;
IF V_ID < 64 THEN
UPDATE TAB1 SET AT2 = 'LEAVE'
WHERE ID = V_ID;
ELSE
LEAVE FETCH_LOOP;
END IF;
END LOOP FETCH_LOOP;
CLOSE CUR1;
END
BEGIN ATOMIC
CALL LEAVE_EX;
END
Solution 1:
In SQL Server, the BREAK statement has similar functionality to the LEAVE statement in DB2, so use it
instead of LEAVE. You do not need to use a label in the most inner loop - using just the BREAK statement is
sufficient.
WHILE 1 = 1
BEGIN
FETCH CUR1 INTO @V_ID;
Guide to Migrating from DB2 to SQL Server and Azure SQL DB Page | 100
IF @@FETCH_STATUS <> 0
BREAK;
CLOSE CUR1;
DEALLOCATE CUR1;
END;
GO
EXECUTE LEAVE_EX;
GO
DB2 Example 2:
A LEAVE statement can be issued from a nested block to leave a statement at a higher level. In the following
example, the LEAVE statement within the LAB2 compound statement causes the LAB1 LOOP statement to
terminate:
LAB1: LOOP
...
LAB2: LOOP
SET A = 0;
...
LAB3: LOOP
...
LEAVE LAB1; -- MULTILEVEL LEAVE
...
END LOOP LAB3;
...
LEAVE LAB1; -- MULTILEVEL LEAVE
...
END LOOP LAB2;
END LOOP LAB1;
Solution 2:
In SQL Server, we need to mark the very first statement below the labeled loop with a constructed label
(adding suffix _leave sounds as a reasonable choice) and use GOTO instead of LEAVE
Guide to Migrating from DB2 to SQL Server and Azure SQL DB Page | 101
LAB3:
WHILE 1 = 1
BEGIN
...
GOTO LAB1_LEAVE -- MULTILEVEL LEAVE EMULATED BY GOTO
...
END
...
GOTO LAB1_LEAVE -- MULTILEVEL LEAVE EMULATED BY GOTO
...
END
END
In DB2, the LOOP statement repeats the execution of a statement or a group of statements.
Links: DB2 for Linux UNIX and Windows 10.5.0, DB2 for z/OS 11.0.0, SQL Server 2014.
DB2 Example:
CREATE PROCEDURE LOOP_EX
BEGIN
DECLARE V_ID INTEGER DEFAULT 0;
LAB1:
LOOP
SET V_ID = V_ID + 1;
IF V_ID = 100 THEN
LEAVE LAB1;
END IF;
END LOOP LAB1;
RETURN V_ID;
END
BEGIN ATOMIC
CALL LOOP_EX;
END
Solution:
In SQL Server, you can organize the LOOP statement with the WHILE statement. In SQL Server the WHILE
statement can repeats execution of an SQL statement or statement block. To define a statement block, use
the control-of-flow keywords BEGIN and END.
Guide to Migrating from DB2 to SQL Server and Azure SQL DB Page | 102
BREAK;
END;
RETURN @V_ID
END;
GO
EXECUTE LOOP_EX;
GO
In DB2, the REPEAT statement executes a statement or a group of statements until a search condition is
true.
Links: DB2 for Linux UNIX and Windows 10.5.0, DB2 for z/OS 11.0.0, SQL Server 2014.
DB2 Example:
CREATE PROCEDURE REPEAT_EX
BEGIN
DECLARE V_ID INTEGER;
DECLARE END_R SMALLINT DEFAULT 0;
DECLARE CUR1 CURSOR FOR
SELECT ID FROM TAB1 ORDER BY ID;
OPEN CUR1;
FETCH_LOOP:
REPEAT
FETCH CUR1 INTO V_ID;
IF V_ID < 64 THEN
UPDATE TAB1 SET AT2 = 'REPEAT'
WHERE ID = V_ID;
ELSE
SET END_R = 1;
END IF;
UNTIL END_R <> 0
END REPEAT FETCH_LOOP;
CLOSE CUR1;
END
BEGIN ATOMIC
CALL REPEAT_EX;
END
Solution:
In SQL Server, the WHILE statement provides similar functionality to the REPEAT statement in DB2, so use
it to emulate REPEAT. Because REPEAT is a cycle with a post-condition, you must check the exit-condition
at the end of the cycle.
Guide to Migrating from DB2 to SQL Server and Azure SQL DB Page | 103
OPEN CUR1;
WHILE 1 = 1
BEGIN
FETCH CUR1 INTO @V_ID;
IF @@FETCH_STATUS <> 0
BREAK;
EXECUTE REPEAT_EX;
GO
In DB2, the RESIGNAL statement is used within a condition handler to resignal the condition that activated
the handler, or to raise an alternate condition so that it can be processed at a higher level. It causes an
exception, warning, or not found condition to be returned, along with optional message text.
Links: DB2 for Linux UNIX and Windows 10.5.0, DB2 for z/OS 11.0.0, SQL Server 2014.
DB2 Example:
CREATE PROCEDURE RESIGNAL_EX(IN V INTEGER)
BEGIN
DECLARE A INTEGER;
DECLARE CONTINUE HANDLER FOR SQLSTATE '22012'
BEGIN
RESIGNAL SQLSTATE '75000'
SET MESSAGE_TEXT = 'NOT ZERO PARAMETER IS REQUIRED.';
END;
BEGIN ATOMIC
CALL RESIGNAL_EX(0);
END
Solution:
In SQL Server, the THROW statement provides similar functionality to the RESIGNAL statement in DB2, so
use it to emulate RESIGNAL. For THROW statement error_number is int and must be greater than or equal
to 50000 and less than or equal to 2147483647, message is nvarchar(2048). RESIGNAL without parameters
should be converted into THROW without parameters. In SQL Server is absent possibility to create and use
condition unlike DB2.
Guide to Migrating from DB2 to SQL Server and Azure SQL DB Page | 104
SQL Server Example:
CREATE PROCEDURE RESIGNAL_EX(@V INTEGER)
AS
BEGIN
DECLARE @A INTEGER;
BEGIN TRY
SELECT @A = 1000/@V;
END TRY
BEGIN CATCH
THROW 50000, 'NOT ZERO PARAMETER IS REQUIRED.', 1;
END CATCH;
END;
GO
EXEC RESIGNAL_EX 0;
GO
In DB2, the RETURN statement is used to return from a routine. For SQL Server functions or methods, it
returns the result of the function or method. For a SQL Server procedure, it optionally returns an integer
status value.
Links: DB2 for Linux UNIX and Windows 10.5.0, DB2 for z/OS 11.0.0, SQL Server 2014.
3.5.3.11.1 RETURN Statement in Scalar Functions or Procedures
If the routine is a function or method, then an expression, NULL, or fullselect must be specified after the
RETURN keyword, and the data type of the result must be assignable to the RETURNS type of the routine. A
procedure cannot return NULL or a fullselect.
DB2 Example:
CREATE FUNCTION RETURN_EX(X DOUBLE)
RETURNS DOUBLE
RETURN SIN(X)/COS(X)
Solution:
When a routine is a scalar function, the SQL Server RETURN statement can be used as in DB2.
SELECT DBO.RETURN_EX(100)
Guide to Migrating from DB2 to SQL Server and Azure SQL DB Page | 105
3.5.3.16 RETURN Statement in Table Functions
DB2 functions can return a table or row. This feature is supported in SQL Server, but the syntax is different.
Row should be emulated by table.
DB2 Example1:
CREATE FUNCTION TABLE_FUNCTION_EX(V_ID INTEGER)
RETURNS TABLE (ID INTEGER,
AT1 INTEGER,
AT2 VARCHAR(200),
AT3 DOUBLE)
RETURN
SELECT ID, AT1, AT2, AT3
FROM TAB1
WHERE ID = V_ID
DB2 Example2:
CREATE TYPE TAB1ROW AS ROW ANCHOR ROW OF TAB1;
BEGIN ATOMIC
CALL ROW_EX;
END
Solution:
When a SQL Server function returns a table or row value, the function must be declared with the RETURNS
TABLE keyword. In both cases function return the table, but for row emulation the table will be consist one
row only. In inline table-valued functions (see Example1), the TABLE return value is defined through a single
SELECT statement. Inline functions do not have associated return variables. In multistatement table-valued
functions (see Example2), returned variable is a TABLE variable, used to store and accumulate the rows that
should be returned as the value of the function.
In DB2, the SIGNAL statement is used to signal an error or warning condition. It causes an error or warning
to be returned with the specified SQLSTATE, along with optional message text.
Links: DB2 for Linux UNIX and Windows 10.5.0, DB2 for z/OS 11.0.0, SQL Server 2014.
DB2 Example:
CREATE PROCEDURE SIGNAL_EX(IN V INTEGER)
BEGIN
IF V IS NULL THEN
SIGNAL SQLSTATE '75000'
SET MESSAGE_TEXT = 'NOT NULL PARAMETER IS REQUIRED.';
END IF;
END
BEGIN ATOMIC
CALL SIGNAL_EX(NULL);
END
Solution:
In SQL Server, the THROW statement provides similar functionality to the SIGNAL statement in DB2, so use
it to emulate SIGNAL. For THROW statement error_number is int and must be greater than or equal to 50000
and less than or equal to 2147483647, message is nvarchar(2048). In SQL Server is absent possibility to
create and use condition unlike DB2.
Guide to Migrating from DB2 to SQL Server and Azure SQL DB Page | 107
END
GO
The WHILE statement repeats the execution of a statement or group of statements while a specified
condition is true.
Links: DB2 for Linux UNIX and Windows 10.5.0, DB2 for z/OS 11.0.0, SQL Server 2014.
DB2 Example:
CREATE PROCEDURE WHILE_EX
BEGIN
DECLARE V_ID INTEGER;
DECLARE END_R SMALLINT DEFAULT 1;
DECLARE CUR1 CURSOR FOR
SELECT ID FROM TAB1 ORDER BY ID;
OPEN CUR1;
FETCH_LOOP:
WHILE END_R <> 0
DO
FETCH CUR1 INTO V_ID;
IF V_ID < 64 THEN
UPDATE TAB1 SET AT2 = 'WHILE'
WHERE ID = V_ID;
ELSE
SET END_R = 0;
END IF;
END WHILE FETCH_LOOP;
CLOSE CUR1;
END
BEGIN ATOMIC
CALL WHILE_EX;
END
Solution:
The WHILE statement can be converted almost as is, although it has a slightly different syntax in SQL
Server. In SQL Server the WHILE statement can repeats execution of an SQL statement or statement block.
To define a statement block, use the control-of-flow keywords BEGIN and END.
Guide to Migrating from DB2 to SQL Server and Azure SQL DB Page | 108
FETCH CUR1 INTO @V_ID;
IF @@FETCH_STATUS <> 0
BREAK;
EXECUTE WHILE_EX;
GO
3.5.4 Cursors
This section covers differences between DB2 and SQL Server versions of cursor implementation, and gives
some hints about how to handle cursors during migration.
DB2 supports static, forward-only, and scrollable cursors. There are two types of scrollable cursor: static and
keyset-driven. The latter provides the ability to detect or make changes to the underlying data.
SQL Server supports all ANSI-style cursors: static, dynamic, forward only, and keyset-driven. This includes
support for INSENSITIVE and SCROLL cursor behavior and for all fetch options (FIRST, LAST, NEXT, PRIOR,
RELATIVE, and ABSOLUTE).
Cursor support is available through the following interfaces: ADO.NET, OLE DB, ODBC, DB-Library, and
Transact-SQL.
In DB2, the CLOSE statement closes a cursor. If a result table was created when the cursor was opened, that
table is destroyed.
Links: DB2 for Linux UNIX and Windows 10.5.0, DB2 for z/OS 11.0.0, SQL Server 2014.
The SQL Server CLOSE CURSOR statement closes the cursor but leaves the data structures accessible for
reopening.
DB2 Example:
CLOSE MYCUR;
Solution:
SQL Server requires the DEALLOCATE CURSOR statement to remove the cursor data structures. The
DEALLOCATE CURSOR statement differs from CLOSE CURSOR in that a closed cursor can be reopened.
The DEALLOCATE CURSOR statement releases all data structures associated with the cursor and removes
the definition of the cursor.
Guide to Migrating from DB2 to SQL Server and Azure SQL DB Page | 109
3.5.4.2 Returning a Result Set
DB2 always requires that cursors be used with SELECT statements, regardless of the number of rows
requested from the database.
Links: DB2 for Linux UNIX and Windows 10.5.0, DB2 for z/OS 11.0.0, SQL Server 2014.
DB2 Example :
CREATE PROCEDURE CURSOR_TO_CLIENT
RESULT SETS 1
BEGIN
DECLARE MYCUR CURSOR WITH RETURN TO CLIENT FOR
SELECT ID, VAL
FROM TABLE_FF;
OPEN MYCUR;
END;
BEGIN ATOMIC
CALL CURSOR_TO_CLIENT;
END;
Solution:
In SQL Server, a SELECT statement that is not enclosed within a cursor returns rows to the client as a
default result set. This is an efficient way to return data to a client application.
EXEC CURSOR_TO_CLIENT;
GO
In DB2, all open cursors are automatically closed when the thread terminates, or when a rollback occurs, or
when a commit is done—except if the cursor is defined "with hold." If the cursor is declared "with hold," it will
remain open after a commit; otherwise it will be closed at commit time.
Solution:
In SQL Server, the cursor is not automatically closed on commit or on rollback. To emulate DB2 behavior,
manually close all cursors after rollback, and close all cursors after commit—except the cursors defined as
"with hold."
In DB2, cursor loops can be organized using handlers. After the handler is invoked successfully, control is
returned to the SQL statement that follows the statement that raised the exception.
Links: DB2 for Linux UNIX and Windows 10.5.0, DB2 for z/OS 11.0.0, SQL Server 2014.
Guide to Migrating from DB2 to SQL Server and Azure SQL DB Page | 110
DB2 Example 1:
CREATE PROCEDURE CURSOR_LOOP_1
BEGIN
DECLARE S DECIMAL(9,2);
DECLARE I DECIMAL(9,2);
DECLARE EXITCODE INTEGER DEFAULT 0;
DECLARE NO_MORE_ROWS CONDITION FOR SQLSTATE '02000';
DECLARE MYCUR CURSOR FOR
SELECT ID
FROM TABLE_FF ORDER BY ID;
DECLARE CONTINUE HANDLER FOR NO_MORE_ROWS
SET EXITCODE = 1;
SET S = 0;
OPEN MYCUR;
FETCH MYCUR INTO I;
WHILE EXITCODE <> 1
DO
SET S = S + I;
FETCH MYCUR INTO I;
END WHILE;
CLOSE MYCUR;
END
BEGIN ATOMIC
CALL CURSOR_LOOP_1;
END;
Solution:
SQL Server does not provide such exceptions to emulate cursor loops using handlers emulating. To emulate
this behavior, add the following block after each FETCH statement:
IF(@@FETCH_STATUS <> 0)
BEGIN
SET @EXITCODE = 1 --HANDLER BODY
END
EXEC CURSOR_LOOP_1;
GO
In DB2, a cursor loop can be organized by using a direct check of the SQLCODE after each FETCH
statement.
Links: DB2 for Linux UNIX and Windows 10.5.0, DB2 for z/OS 11.0.0, SQL Server 2014.
DB2 Example:
CREATE PROCEDURE CURSOR_LOOP_2
BEGIN
DECLARE SQLCODE INTEGER DEFAULT 0;
DECLARE I DECIMAL(9,2);
DECLARE S DECIMAL(9,2);
DECLARE MYCUR CURSOR FOR
SELECT ID
FROM TABLE_FF ORDER BY ID;
SET S = 0;
OPEN MYCUR;
FETCH MYCUR INTO I;
WHILE SQLCODE <> 100
DO
SET S = S + I;
FETCH MYCUR INTO I;
END WHILE;
CLOSE MYCUR;
END;
BEGIN ATOMIC
CALL CURSOR_LOOP_2;
END;
Solution:
In SQL Server, analyze the string before SQLCODE <> 100. If it is a FETCH statement, then replace
SQLCODE <> 100 with @@FETCH_STATUS = 0:
FETCH MYCUR INTO I; --\ FETCH MYCUR INTO @I;
WHILE SQLCODE <> 100 --/ WHILE @@FETCH_STATUS = 0
EXEC CURSOR_LOOP_2;
GO
The CURSOR_ROWCOUNT function returns the cumulative count of all rows fetched by the specified
cursor since the cursor was opened.
Links: DB2 for Linux UNIX and Windows 10.5.0, SQL Server 2014.
DB2 Example:
CREATE PROCEDURE CURSOR_ROWCOUNT_FUNC()
BEGIN
DECLARE ROWS_FETCH BIGINT;
DECLARE ID DECIMAL(9,2);
DECLARE EOF INT DEFAULT 0;
DECLARE MYCUR CURSOR;
OPEN MYCUR;
Guide to Migrating from DB2 to SQL Server and Azure SQL DB Page | 113
CLOSE MYCUR;
END;
BEGIN ATOMIC
CALL CURSOR_ROWCOUNT_FUNC;
END;
Solution:
In SQL Server, you could emulate DB2 CURSOR_ROWCOUNT scalar function with the simple variable. After
each FETCH increase the variable.
OPEN @MYCUR;
PRINT @ROWS_FETCH
CLOSE @MYCUR;
DEALLOCATE @MYCUR;
END;
GO
EXEC CURSOR_ROWCOUNT_FUNC;
GO
Guide to Migrating from DB2 to SQL Server and Azure SQL DB Page | 114
3.5.5 Variables
3.5.5.1 Variable Declaration
In DB2, local variable support in SQL procedures allows you to assign and retrieve SQL values in support of
SQL procedure logic. Variables in SQL procedures are defined by using the DECLARE statement. When
declaring a variable, you can specify a default value using the DEFAULT clause.
Links: DB2 for Linux UNIX and Windows 10.5.0, DB2 for z/OS 11.0.0, SQL Server 2014.
DB2 Example:
BEGIN ATOMIC
DECLARE V_RCOUNT INTEGER;
DECLARE V_MAX DECIMAL (9,2);
DECLARE V_ADATE, V_ANOTHER DATE;
DECLARE V_TOTAL INTEGER DEFAULT 0;
END;
Solution:
In SQL Server, variables are declared in the body of a batch or procedure with the DECLARE statement.
Variable names must begin with an ‘at’ (@) sign. You can declare variables by list, but data type must be
defined for each variable in this list. You can assign a value to the variable in-line. The value can be a
constant or an expression, but it must either match the variable declaration type or be implicitly convertible to
that type.
DB2 Example:
CREATE PROCEDURE PRC_EXIT
LANGUAGE SQL
BEGIN ATOMIC
DECLARE RETURN_MESSAGE VARCHAR(100) DEFAULT 0;
DECLARE RETURN_CODE INTEGER DEFAULT 0;
DECLARE V_ID INTEGER;
DECLARE V_NAME VARCHAR(100);
DECLARE C1 CURSOR FOR SELECT ID, NAME FROM EMPLOYEES;
Guide to Migrating from DB2 to SQL Server and Azure SQL DB Page | 115
/*** HANDLER DECLARATION ***/
DECLARE EXIT HANDLER FOR NOT FOUND BEGIN
/*** HANDLER STATEMENTS ***/
SET RETURN_MESSAGE = 'ERROR. ROWS HAVE BEEN COMPLITED.';
SET RETURN_CODE = 200;
INSERT INTO MES(GROUP_ID, TEXT)VALUES('PRC_EXIT',
'RETURN_MESSAGE='||RETURN_MESSAGE);
CLOSE C1;
END;
Solution:
In SQL Server, use a TRY/CATCH block. Because exception doesn’t occur in SQL Server after unsuccessful
fetch data from cursor, so we need to check the @@fetch_status variable and generate a manual exception.
Code-statements need to place in try-block and handler-statements need to place in catch-block.
DB2 Example:
/*** CREATE PROCEDURE ***/
CREATE PROCEDURE PRC_UNDO
LANGUAGE SQL
BEGIN ATOMIC
DECLARE RETURN_MESSAGE VARCHAR(100) DEFAULT 0;
DECLARE RETURN_CODE INTEGER DEFAULT 0;
DECLARE V_ID INTEGER;
DECLARE V_NAME VARCHAR(100);
DECLARE C1 CURSOR FOR SELECT ID, NAME FROM EMPLOYEES;
Guide to Migrating from DB2 to SQL Server and Azure SQL DB Page | 117
Solution:
In SQL Server, specify a savepoint after the BEGIN statement and the ROLLBACK statement in a CATCH
block.
BEGIN TRY
/*** CODE STATEMENTS ***/
OPEN C1;
WHILE 1=1 BEGIN
FETCH C1 INTO @V_ID, @V_NAME; IF (@@FETCH_STATUS <> 0)
THROW 51000, '', 1; /* THIS CLAUSE GENERATES USER’S EXCEPTION */
INSERT INTO MES(GROUP_ID, TEXT)VALUES('PRC_UNDO', 'GOOD:
V_NAME='+@V_NAME);
END;
CLOSE C1;
DEALLOCATE C1;
END TRY
BEGIN CATCH
Guide to Migrating from DB2 to SQL Server and Azure SQL DB Page | 118
END;
SELECT * FROM MES ORDER BY ID;
DB2 Example:
CREATE PROCEDURE DB2_OBJECTS.CONT
LANGUAGE SQL
BEGIN ATOMIC
DECLARE X INTEGER;
DECLARE SQLSTATE CHAR(5) DEFAULT '00000'; /* IT IS REGISTER
SQLCODE */
DECLARE SQLCODE INTEGER DEFAULT 0; /* IT IS REGISTER SQLSTATE
*/
DECLARE RSTATE CHAR(5) DEFAULT '00000';
DECLARE RCODE INTEGER DEFAULT 0;
DECLARE ZERO CONDITION FOR SQLSTATE '22012';
/* FIRST STATEMENT */
SET X = 1/0; /* MAKE AN EXCEPTION DIVISION BY ZERO */
/* SECOND STATEMENT */
SET RESULT = (SELECT CAST('12' AS INTEGER) FROM SYSIBM.SYSDUMMY1);
/* THIRD STATEMENT */
SET RESULT = (SELECT COS(0)/SIN(0) FROM SYSIBM.SYSDUMMY1); /* MAKE
AN EXCEPTION DIVISION BY ZERO */
END;
Solution:
In SQL Server, use the same syntax as in the EXIT handler, but wrap every single statement in a
TRY/CATCH block.
Guide to Migrating from DB2 to SQL Server and Azure SQL DB Page | 119
DECLARE @V_ERR NUMERIC(38);
DECLARE @RCODE VARCHAR(10);
DECLARE @RSTATE VARCHAR(10);
DECLARE @SQLERRM VARCHAR(8000);
DECLARE @SQLERRM1 VARCHAR(8000);
DECLARE @SSMA$ZERO_CONDITION VARCHAR(10) = '22012'; /* DECLARE
CONDITION */
/* FIRST STATEMENT */
BEGIN TRY
SET @X = 1/0; /* MAKE AN EXCEPTION DIVISION BY ZERO */
END TRY
BEGIN CATCH
SET @V_ERR = ERROR_NUMBER();
SET @RCODE = SSMA_DB2.SQLCODE(@V_ERR);
SET @RSTATE = SSMA_DB2.SQLSTATE(@V_ERR);
SET @SQLERRM = SSMA_DB2.SQLERRM(@RSTATE, '', '', 'EN_US', 1);
SET @SQLERRM1= SSMA_DB2.SQLERRM (REPLACE(@RCODE,'-','SQL'),
'', '', 'EN_US', 1);
IF @RSTATE != @SSMA$ZERO_CONDITION BEGIN /* CHECK ON CONDITION
*/
THROW;
END;
INSERT INTO MES(GROUP_ID, TEXT)VALUES('PRC_EXIT',
'RCODE='+@RCODE+'; RSTATE='+@RSTATE+'; ERRM='+@SQLERRM);
INSERT INTO MES(GROUP_ID, TEXT)VALUES('PRC_EXIT',
'RCODE='+@RCODE+'; RSTATE='+@RSTATE+'; ERRM='+@SQLERRM1);
END CATCH;
/* SECOND STATEMENT */
BEGIN TRY
SELECT RESULT = CAST('12' AS INTEGER);
END TRY
BEGIN CATCH
SET @V_ERR = ERROR_NUMBER();
SET @RCODE = [SSMA_DB2].[SQLCODE](@V_ERR);
SET @RSTATE = [SSMA_DB2].[SQLSTATE](@V_ERR);
SET @SQLERRM = SSMA_DB2.SQLERRM(@RSTATE, '', '', 'EN_US', 1);
SET @SQLERRM1= SSMA_DB2.SQLERRM (REPLACE(@RCODE,'-','SQL'),
'', '', 'EN_US', 1);
IF @RSTATE != @SSMA$ZERO_CONDITION BEGIN /* CHECK ON
CONDITION. */
THROW;
END;
INSERT INTO MES(GROUP_ID, TEXT)VALUES('PRC_EXIT',
'RCODE='+@RCODE+'; RSTATE='+@RSTATE+'; ERRM='+@SQLERRM);
INSERT INTO MES(GROUP_ID, TEXT)VALUES('PRC_EXIT',
'RCODE='+@RCODE+'; RSTATE='+@RSTATE+'; ERRM='+@SQLERRM1);
END CATCH;
/* THIRD STATEMENT */
BEGIN TRY
SELECT RESULT = COS(0)/SIN(0); /* MAKE AN EXCEPTION DIVISION
BY ZERO */
END TRY
Guide to Migrating from DB2 to SQL Server and Azure SQL DB Page | 120
BEGIN CATCH
SET @V_ERR = ERROR_NUMBER();
SET @RCODE = [SSMA_DB2].[SQLCODE](@V_ERR);
SET @RSTATE = [SSMA_DB2].[SQLSTATE](@V_ERR);
SET @SQLERRM = SSMA_DB2.SQLERRM(@RSTATE, '', '', 'EN_US', 1);
SET @SQLERRM1= SSMA_DB2.SQLERRM (REPLACE(@RCODE,'-','SQL'),
'', '', 'EN_US', 1);
IF @RSTATE != @SSMA$ZERO_CONDITION BEGIN /* CHECK ON CONDITION
*/
THROW;
END;
INSERT INTO MES(GROUP_ID, TEXT)VALUES('PRC_EXIT',
'RCODE='+@RCODE+'; RSTATE='+@RSTATE+'; ERRM='+@SQLERRM);
INSERT INTO MES(GROUP_ID, TEXT)VALUES('PRC_EXIT',
'RCODE='+@RCODE+'; RSTATE='+@RSTATE+'; ERRM='+@SQLERRM1);
END CATCH;
END;
GO
Solution:
In SQL Server, there is no direct solution to emulate the DESCRIBE statement or SQLDA. Instead, change
these statements to another type of dynamic SQL in an application using varying-list SELECT, and then
convert.
DB2 Example:
CREATE PROCEDURE PREPARE_EX
BEGIN
DECLARE STMT VARCHAR(1000);
SET STMT = 'INSERT INTO TAB1(ID, AT1, AT2, AT3) VALUES(100, 1,
''1'', 1)';
PREPARE STMT_EX FROM STMT;
EXECUTE STMT_EX;
END;
Guide to Migrating from DB2 to SQL Server and Azure SQL DB Page | 121
BEGIN ATOMIC
CALL PREPARE_EX;
END;
Solution:
SQL Server prepares and caches SQL statement only after execution. Therefore you can remove the
PREPARE statement, and replace the EXECUTE statement syntax with the SQL Server EXECUTE
statement.
DB2 Example:
CREATE PROCEDURE DYNAMIC_EX (
IN DYN_T VARCHAR(20), IN DYN_V VARCHAR(100), IN VAL INTEGER)
BEGIN
DECLARE STMT VARCHAR(1000);
SET STMT = 'INSERT INTO ' || DYN_T ||
' (ID, AT1, AT2, AT3) VALUES( ?, ' || DYN_V || ')';
PREPARE STMT_EX FROM STMT;
WHILE (VAL < 200)
DO
EXECUTE STMT_EX USING VAL;
SET VAL = VAL + 10;
END WHILE;
END;
BEGIN ATOMIC
DECLARE TLIST VARCHAR(20);
DECLARE VLIST VARCHAR(100);
DECLARE VAL INTEGER;
SET TLIST = 'TAB1';
SET VLIST = '1, ''1'', 1';
SET VAL = 110;
CALL DYNAMIC_EX(TLIST, VLIST, VAL);
END;
Solution:
In SQL Server, you can use the EXECUTE statement without previous use of the PREPARE statement. To
convert a non-SELECT or caching dynamic SQL statement, you need to generate a dynamic string using a
local variable and execute the string using the EXECUTE statement.
Guide to Migrating from DB2 to SQL Server and Azure SQL DB Page | 122
SQL Server Example:
CREATE PROCEDURE DYNAMIC_EX (
@DYN_T VARCHAR(20), @DYN_V VARCHAR(100), @VAL INTEGER)
AS
BEGIN
DECLARE @STMT VARCHAR(1000);
WHILE (@VAL < 200)
BEGIN
SET @STMT = 'INSERT INTO ' + @DYN_T +
' (ID, AT1, AT2, AT3) VALUES( ' + CAST(@VAL AS VARCHAR(10)) +
', ' + @DYN_V + ')';
EXECUTE (@STMT);
SET @VAL = @VAL + 10;
END;
END;
GO
DB2 Example:
CREATE PROCEDURE DYNAMIC_IMMEDIATE_EX (
IN DYN_T VARCHAR(20), IN DYN_V VARCHAR(100))
BEGIN
DECLARE STMT VARCHAR(1000);
SET STMT = 'INSERT INTO ' || DYN_T || ' ' || DYN_V;
EXECUTE IMMEDIATE STMT;
END;
BEGIN ATOMIC
DECLARE TLIST VARCHAR(20);
DECLARE VLIST VARCHAR(100);
SET TLIST = 'TAB1';
SET VLIST = '(ID, AT1, AT2, AT3) VALUES(200, 1, ''1'', 1)';
CALL DYNAMIC_IMMEDIATE_EX (TLIST, VLIST);
END;
Solution:
In SQL Server, the EXECUTE IMMEDIATE statement can be converted directly to the EXECUTE statement.
There are only minor differences between DB2 and SQL Server in the syntax, such as in declaring variables
and calling procedures.
Guide to Migrating from DB2 to SQL Server and Azure SQL DB Page | 123
SQL Server Example:
CREATE PROCEDURE DYNAMIC_IMMEDIATE_EX (@DYN_T VARCHAR(20), @DYN_V
VARCHAR(100))
AS
DECLARE @STMT VARCHAR(1000);
SET @STMT = 'INSERT INTO ' + @DYN_T + ' ' + @DYN_V;
EXECUTE (@STMT);
GO
DB2 Example:
CREATE PROCEDURE DYNAMIC_FIXED_LIST_EX (IN VAL INTEGER, OUT V_ID INTEGER,
OUT V_AT1 INTEGER, OUT V_AT2 VARCHAR(200), OUT V_AT3 DOUBLE)
BEGIN
DECLARE STMT VARCHAR(1000);
DECLARE CUR1 CURSOR FOR STMT_EX;
SET STMT = 'SELECT ID, AT1, AT2, AT3 FROM TAB1 WHERE ID = ?';
PREPARE STMT_EX FROM STMT;
OPEN CUR1 USING VAL;
FETCH CUR1 INTO V_ID, V_AT1, V_AT2, V_AT3;
CLOSE CUR1;
END;
BEGIN ATOMIC
DECLARE VAL, VID, VAT1 INTEGER;
DECLARE VAT2 VARCHAR(200);
DECLARE VAT3 DOUBLE;
SET VAL = 60;
CALL DYNAMIC_FIXED_LIST_EX(VAL, VID, VAT1, VAT2, VAT3);
END;
Solution:
In SQL Server, to convert dynamic SQL for a fixed-list SELECT statement you can use the sp_executesql
stored procedure, This procedure makes it possible to create a cursor based on a dynamic string, and to use
local variables as parameters of this cursor.
Guide to Migrating from DB2 to SQL Server and Azure SQL DB Page | 124
@V_AT2 VARCHAR(200) OUTPUT,
@V_AT3 FLOAT(53) OUTPUT
)
AS
DECLARE @STMT NVARCHAR(1000);
DECLARE @PARAMS_DEFINITION NVARCHAR(500);
DECLARE @CUR1 CURSOR;
SET @STMT = (N'SET @CUR = CURSOR LOCAL FOR '+
'SELECT ID, AT1, AT2, AT3 FROM TAB1 WHERE ID = @VAL; OPEN @CUR');
SET @PARAMS_DEFINITION = N'@VAL INTEGER, @CUR CURSOR OUTPUT';
EXECUTE SP_EXECUTESQL
@STMT,
@PARAMS_DEFINITION,
@VAL = @V_VAL,
@CUR = @CUR1 OUTPUT;
FETCH @CUR1 INTO @V_ID, @V_AT1, @V_AT2, @V_AT3;
CLOSE @CUR1;
DEALLOCATE @CUR1
GO
Solution:
In SQL Server there is no solution to emulate SQLDA. Instead, change this statement to another type of
dynamic SQL in an application using varying-list SELECT, and then convert.
3.8 Aliases
A DB2 alias can be defined for a table, view, nickname, or another alias. The alias should be created in the
same database where the object for which it was created exists.
Links:
DB2 for Linux UNIX and Windows 10.5.0, DB2 for z/OS 11.0.0, SQL Server 2014
DB2 Example:
CREATE ALIAS DB2OBJECTS.A1 FOR DB2OBJECTS.EMPLOYEES; /* CREATE ALIAS FOR
TABLE */
CREATE ALIAS DB2OBJECTS.A2 FOR DB2OBJECTS.A1; /* CREATE ALIAS FOR
ALIAS */
3.9 Nicknames
In DB2, a nickname can be used to access objects either from other databases or from non-relational data
sources. Both cases are discussed below.
DB2 Example:
CREATE NICKNAME DEPT FOR SALE.BUH.DEPARTMENT;
Solution:
SQL Server allows three-level object names in the format <database-name>.<schema-name>.<object-
name>. You can convert DB2 nicknames that have been defined for tables, views, or stored procedures by
using SQL Server synonyms. For remote databases, a SQL Server linked server object should be created.
DB2 Example:
CREATE NICKNAME DATA1
(DCODE INTEGER,
NAME CHAR(20),
DEPARTMENT CHAR(20))
FOR SERVER DEPTS
OPTIONS
(FILE_PATH ’/USR/PAT/DATA1.TXT’,
COLUMN_DELIMITER ’,’,
Guide to Migrating from DB2 to SQL Server and Azure SQL DB Page | 126
KEY_COLUMN ’DCODE’,
SORTED ’Y’,
VALIDATE_DATA_FILE ’Y’);
Solution:
In SQL Server, the solution is the same as for DB2, except that you must create a linked server for the
nonrelational data source; in this example, the nonrelational data source is a text file. SQL Server makes this
possible using the OLE DB provider mechanism.
Note that this code is not applicable on Azure SQL DB as this version of SQL Server doesn’t support working
with file system.
A DB2 distinct type is a user-defined type that is based on an existing DB2 built-in data type. Internally, a
distinct type shares its representation with an existing type (the source type), but is considered to be a separate
and incompatible type. Values with a user-defined distinct type can only be compared with values of exactly
the same user-defined distinct type. The user-defined distinct type must have been defined using the WITH
COMPARISONS clause.
DB2 Example:
CREATE DISTINCT TYPE DB2_UDT.DISTINCT_TYPE AS VARCHAR (100) WITH
COMPARISONS
Solution:
In SQL Server, user-defined types are the same as DB2 user-defined distinct types. The WITH
COMPARISONS clause can be omitted.
DB2 Example1:
CREATE TYPE DB2_UDT.STRUCTURED_TYPE AS (DEPT_NAME VARCHAR(100), MAX_EMPS
INT) MODE DB2SQL;
DB2 EXAMPLE2:
Solution:
In SQL Server, a table type is a user-defined type that can partially emulate a DB2 typed table. However,
SQL Server does not allow using table types as the types for columns in a table. In SQL Server a table type
can be used to emulate a DB2 structured type only when the structured type is used as a local variable, or
when a typed table is based on the structured type whose attributes are DB2 base types.
If, in DB2, a definition of a structured type on which a DB2 table is based has the REF USING rep-type
expression, then in SQL Server you should add a primary key or unique constraint that contains the object
identifier (OID) attribute of rep-type type to the SQL Server table type definition when converting a table of this
type. In this situation, you should perform the conversion of DB2 base types to SQL Server by using type
mapping.
If there is no REF USING expression in the definition of the DB2 structured type, then the OID attribute
should be defined as varbinary(16) in SQL Server. You do not need to add an OID field and a key in a table
type in SQL Server when you are converting a structured type that is used as a local variable.
There is no SQL Server solution yet to emulate DB2 typed views. To emulate reference types and
structured type method hierarchies, you should use SQL Server CLR user-defined types.
Guide to Migrating from DB2 to SQL Server and Azure SQL DB Page | 128
CREATE TYPE STRUCTURED_TYPE AS TABLE
( DEPT_NAME VARCHAR(20),
MAX_EMPS INTEGER);
Links: DB2 for Linux UNIX and Windows 10.5.0, DB2 for z/OS 11.0.0, SQL Server 2014.
A structured type may be created under another structured type, in which case the newly created type is a
subtype of the original structured type. The original type is the supertype. The subtype inherits all the attributes
of the supertype, and can optionally have additional attributes of its own.
For example, a data model may need to represent a special type of employee called a manager. Managers
have more attributes than employees who are not managers. The Manager_t type inherits the attributes defined
for an employee, but also is defined with some additional attributes of its own, such as a special bonus attribute
that is only available to managers. The type hierarchies are shown in following figure.
Type hierarchies
DB2 Example:
CREATE TYPE DB2_UDT.HIERACHY_BUSINESSUNIT_T AS (Name VARCHAR(20), Headcount INT)
MODE DB2SQL;
--To create the Person_t type hierarchy, issue the following SQL statements:
CREATE TYPE DB2_UDT.Hierachy_Address_t AS (street VARCHAR(30),number
CHAR(15),city VARCHAR(30), state VARCHAR(20), zip CHAR(10)) MODE DB2SQL;
Links: DB2 for Linux UNIX and Windows 10.5.0, DB2 for z/OS 11.0.0, SQL Server 2014.
The CREATE METHOD statement is used to associate a method body with a method specification that is
already part of the definition of a user-defined structured type. The method specification must be previously
defined using the CREATE TYPE or ALTER TYPE statement before CREATE METHOD.
DB2 Example:
CREATE TYPE DB2_UDT.TYPE_WITH_METHODS AS (PRINCIPLE INT, INTEREST
DECIMAL(5,2), YEAR INT)
NOT FINAL
MODE DB2SQL
METHOD SI()
RETURNS FLOAT
LANGUAGE SQL,
METHOD CI()
RETURNS FLOAT
LANGUAGE SQL,
DB2 Example:
CREATE TYPE DB2_UDT.METHOD_ADDRESS_T AS (STREET VARCHAR(30),NUMBER
CHAR(15),CITY VARCHAR(30), STATE VARCHAR(20), ZIP CHAR(10)) MODE DB2SQL;
CREATE TYPE DB2_UDT.TYPE_WITH_METH_C_LANG AS (STREET VARCHAR(30), NUMBER
CHAR(15), CITY VARCHAR(30), STATE VARCHAR(10))
NOT FINAL
MODE DB2SQL
METHOD SAMEZIP_2 (ADDR DB2_UDT.METHOD_ADDRESS_T)
RETURNS INTEGER
LANGUAGE SQL,
Guide to Migrating from DB2 to SQL Server and Azure SQL DB Page | 130
DETERMINISTIC
PARAMETER STYLE SQL
NO SQL
NO EXTERNAL ACTION
Links: DB2 for Linux UNIX and Windows 10.5.0, DB2 for z/OS 11.0.0, SQL Server 2014.
A reference type is a companion type to a structured type. Similar to a distinct type, a reference type is a
scalar type that shares a common representation with one of the built-in data types. This same representation
is shared for all types in the type hierarchy. The reference type representation is defined when the root type of
a type hierarchy is created. When using a reference type, a structured type is specified as a parameter of the
type. This parameter is called the target type of the reference.
The target of a reference is always a row in a typed table or a typed view. When a reference type is used,
it may have a scope defined. The scope identifies a table (called the target table) or view (called the target
view) that contains the target row of a reference value. The target table or view must have the same type as
the target type of the reference type. An instance of a scoped reference type uniquely identifies a row in a typed
table or typed view, called the target row.
DB2 Example:
CREATE TYPE DB2_UDT.REF_TYPE AS (NAME VARCHAR(30), LOCATION VARCHAR(30))
MODE DB2SQL;
CREATE TABLE DB2_UDT.REF_TYPE_TAB OF DB2_UDT.REF_TYPE (REF IS SUPPNO USER
GENERATED);
INSERT INTO DB2_UDT.REF_TYPE_TAB VALUES
(DB2_UDT.REF_TYPE('1'),'DBDEST','KHARKIV');
INSERT INTO DB2_UDT.REF_TYPE_TAB VALUES
(DB2_UDT.REF_TYPE('2'),'NOVOSVIT','KHARKIV');
An array type is a user-defined data type consisting of an ordered set of elements of a single data type.
A user-defined array type is a data type that is defined as an array with elements of another data type.
Every ordinary array type has an index with the data type of INTEGER and has a defined maximum cardinality.
Every associative array has an index with the data type of INTEGER or VARCHAR and does not have a defined
maximum cardinality.
Array type usage: An array type can only be used as the data type of:
A local variable in a compound SQL (compiled) statement
Guide to Migrating from DB2 to SQL Server and Azure SQL DB Page | 131
A parameter of an SQL routine
A parameter of a Java™ procedure (non-nested ordinary arrays only)
The returns type of an SQL function
A global variable
A variable or parameter defined with an array type can only be used in compound SQL (compiled)
statements
An ordinary array type has a defined upper bound on the number of elements and uses the ordinal position
as the array index.
Links: DB2 for Linux UNIX and Windows 10.5.0, DB2 for z/OS 11.0.0, SQL Server 2014.
DB2 Example:
CREATE TYPE DB2_UDT.SIMPLE_ARRAY AS INTEGER ARRAY[]
An associative array type has no specific upper bound on the number of elements and each element has
an associated index value. The data type of the index value can be an integer or a character string but is the
same data type for the entire array.
Links: DB2 for Linux UNIX and Windows 10.5.0, DB2 for z/OS 11.0.0, SQL Server 2014.
DB2 Example:
CREATE TYPE DB2_UDT.INT_ARRAY AS INTEGER ARRAY[100];
Links: DB2 for Linux UNIX and Windows 10.5.0, DB2 for z/OS 11.0.0, SQL Server 2014.
A row type is a data type that is defined as an ordered sequence of named fields, each with an associated
data type, which effectively represents a row. A row type can be used as the data type for variables and
parameters in SQL PL to provide simple manipulation of a row of data.
Row type usage: A row type can only be used as the data type of:
A local variable in a compound SQL (compiled) statement
A parameter of an SQL routine
The return type of an SQL function
The element of an array type
Guide to Migrating from DB2 to SQL Server and Azure SQL DB Page | 132
The field type in a row type
A user-defined cursor type (only non-nested row types)
A global variable
A variable or parameter defined with a row type can only be used in compound SQL (compiled) statements
DB2 Example:
CREATE TYPE DB2_UDT.ROW_TYPE AS ROW (DEPTNO VARCHAR(3), DEPTNAME
VARCHAR(29), MGRNO CHAR(6));
CALL DB2_UDT.ROW_TYPE_PROC;
Links: DB2 for Linux UNIX and Windows 10.5.0, DB2 for z/OS 11.0.0, SQL Server 2014.
An anchored type defines a data type based on another SQL object such as a column, global variable,
SQL variable, SQL parameter, or the row of a table or view.
A data type defined using an anchored type definition maintains a dependency on the object to which it is
anchored. Any change in the data type of the anchor object will impact the anchored data type. If anchored to
the row of a table or view, the anchored data type is ROW with the fields defined by the columns of the anchor
table or anchor view.
This data type is useful when declaring variables in cases where you require that the variable have the
same data type as another object, for example a column in a table, but you do not know exactly what is the
data type.
An anchored data type can be of the same type as one of:
a row in a table
a row in a view
a cursor variable row definition
a column in a table
a column in a view
a local variable, including a local cursor variable or row variable
a global variable
Anchored data types can only be specified when declaring or creating one of the following:
a local variable in an SQL procedure, including a row variable
a local variable in a compiled SQL function, including a row variable
a routine parameter
a user-defined cursor data type using the CREATE TYPE statement.
It cannot be referenced in a DECLARE CURSOR statement.
a function return data type
a global variable
Guide to Migrating from DB2 to SQL Server and Azure SQL DB Page | 133
DB2 Example:
CREATE PROCEDURE DB2_UDT.ANCHOR_TYPE_VAL()
BEGIN
DECLARE V1 ANCHOR DATA TYPE TO DB2_UDT.ANCHOR_ROW_TYPE.DEPTNAME;
SELECT DEPTNAME INTO V1 FROM DB2_UDT.DEPT_ROW_TYPE;
INSERT INTO DB2_UDT.ANCHOR_ROW_TYPE VALUES (5, V1, 555);
END;
CALL DB2_UDT.ANCHOR_TYPE_VAL();
DB2 Example:
CREATE TYPE DB2_UDT.ANCHOR_TYPE AS ROW ANCHOR DATA TYPE TO ROW OF
DB2_UDT.DEPT_ROW_TYPE;
CALL DB2_UDT.ANCHOR_TYPE();
Links: DB2 for Linux UNIX and Windows 10.5.0, DB2 for z/OS 11.0.0, SQL Server 2014.
A user-defined cursor type is a user-defined data type defined with the keyword CURSOR and optionally
with an associated row type. A user-defined cursor type with an associated row type is astrongly-typed cursor
type; otherwise, it is a weakly-typed cursor type. A value of a user-defined cursor type represents a reference
to an underlying cursor.
Cursor type usage: A cursor type can only be used as the data type of:
A local variable in a compound SQL (compiled) statement
A parameter of an SQL routine
The returns type of an SQL function
A global variable
A variable or parameter defined with a cursor type can only be used in compound SQL (compiled)
statements
A variable or parameter that has a strongly-typed cursor type must not be used to assign cursor values
that are based on a statement-name instead of a select-statement
A user-defined cursor type with an associated row type is a strongly-typed cursor type; otherwise, it is a
weakly-typed cursor type.
DB2 Example:
CREATE TYPE DB2_UDT.ANCHOR_TYPE AS ROW ANCHOR DATA TYPE TO ROW OF
DB2_UDT.DEPT_ROW_TYPE;
CALL DB2_UDT.CURSOR_TYPE(1);
Solution:
In SQL Server, you can emulate the CURRENT TIMESTAMP register by using the CURRENT_TIMESTAMP
or GETDATE() functions.
Solution:
In SQL Server, use SYSDATETIMEOFFSET function.
DB2 Example:
Guide to Migrating from DB2 to SQL Server and Azure SQL DB Page | 135
SELECT CURRENT DATE FROM SYSIBM.SYSDUMMY1
Solution:
In SQL Server, you can emulate CURRENT DATE using the CURRENT_TIMESTAMP function.
DB2 Example:
SELECT CURRENT TIME FROM SYSIBM.SYSDUMMY1
Solution:
In SQL Server, you can emulate CURRENT TIME by using the CURRENT_TIMESTAMP function.
DB2 Example:
SELECT CURRENT TIMEZONE FROM SYSIBM.SYSDUMMY1
Solution:
In SQL Server, you can emulate CURRENT TIMEZONE by using the DATEPART and
SYSDATETIMEOFFSET functions.
Guide to Migrating from DB2 to SQL Server and Azure SQL DB Page | 136
SQL Server Example:
WITH TZ (O) AS (SELECT DATEPART (TZOFFSET, SYSDATETIMEOFFSET ()))
SELECT (O / 60 * 100 + O % 60) * 100 FROM TZ;
DB2 Example:
SELECT CURRENT USER FROM SYSIBM.SYSDUMMY1
Solution:
In SQL Server, you can emulate the CURRENT USER register by using the CURRENT_USER function.
DB2 Example:
SELECT SESSION_USER FROM SYSIBM.SYSDUMMY1
Solution:
In SQL Server, you can emulate SESSION_USER and USER by using the SESSION_USER function.
3.11.8 SYSTEM_USER
In DB2, the SYSTEM_USER special register specifies the authorization ID of the user who is connected to
the database.
Links: DB2 for Linux UNIX and Windows 10.5.0, SQL Server 2014.
DB2 Example:
SELECT SYSTEM_USER FROM SYSIBM.SYSDUMMY1
Solution:
In SQL Server, you can emulate SYSTEM_USER by using the SYSTEM_USER function.
Guide to Migrating from DB2 to SQL Server and Azure SQL DB Page | 137
SQL Server Example:
SELECT SYSTEM_USER
DB2 Example:
SELECT CURRENT CLIENT_APPLNAME FROM SYSIBM.SYSDUMMY1
Solution:
In SQL Server, you can emulate the CURRENT CLIENT_APPLNAME register by using the APP_NAME()
function.
DB2 Example:
SELECT CURRENT CLIENT_WRKSTNNAME FROM SYSIBM.SYSDUMMY1
Solution:
In SQL Server, you can emulate CURRENT CLIENT_WRKSTNNAME by using the HOST_NAME() function.
DB2 Example:
SET CURRENT LOCK TIMEOUT 1800;
SELECT CURRENT LOCK TIMEOUT FROM SYSIBM.SYSDUMMY1;
Solution:
In SQL Server, you can emulate CURRENT LOCK TIMEOUT by using the @@LOCK_TIMEOUT function.
DB2 Example:
SELECT CURRENT SCHEMA FROM SYSIBM.SYSDUMMY1
Solution:
In SQL Server, you can emulate CURRENT SCHEMA by using the db_name() or schema_name() functions.
It depends from mapping type (Schema - Database or Schema - Schema).
DB2 Example:
SELECT CURRENT SERVER FROM SYSIBM.SYSDUMMY1
Solution:
In SQL Server, you can emulate CURRENT SERVER by using the @@SERVERNAME function.
DB2 Example:
SELECT CURRENT ISOLATION FROM SYSIBM.SYSDUMMY1
Solution:
Guide to Migrating from DB2 to SQL Server and Azure SQL DB Page | 139
In SQL Server, you can emulate CURRENT ISOLATION by using the script in the following example. But this
example returns isolation levels used in SQL Server. For more information about how to match a DB2
isolation level to a SQL Server isolation level, see section “Isolation Level and Lock Type”.
3.12 Synonyms
In DB2, a synonym is an alternate name for a table or view. A synonym can be used to reference a table or
view in cases where an existing table or view can be referenced. However, synonyms are deprecated,
therefore we convert them like the aliases. See Aliases for more details.
DB2 Example:
CREATE SYNONYM DB2_OBJECTS.A1 FOR DB2_OBJECTS.EMPLOYEES
Guide to Migrating from DB2 to SQL Server and Azure SQL DB Page | 140
4.0 Migrating DB2 Standard Functions
This section describes how to map DB2 standard functions to equivalent SQL Server functions, and provides
solutions for emulating DB2 functions.
In DB2, the ASCII_CHR function returns the character that has the ASCII code value that is specified by the
argument.
DB2 Example:
ASCII_CHR(65)
Solution:
In SQL Server, use CHAR function.
Guide to Migrating from DB2 to SQL Server and Azure SQL DB Page | 141
DB2 Example:
CHARACTER_LENGTH(‘JOHN SMIT ’, OCTETS)
Solution:
In SQL Server, use LEN function.
The OCTETS as a CODEUNITS is compatible with LEN() only.
Replace trilling spaces or use other way to other character to correct length evaluate.
4.2.2.3 CHR
In DB2, returns the character that has the ASCII code value specified by the argument.
DB2 Example:
CHR(65)
Solution:
In SQL Server, use CHAR function.
4.2.2.4 CONCAT
DB2 Example:
CONCAT(’NUMBER ’, ’1’)
Solution:
In SQL Server, use + (plus) operator.
SQL Server Example:
’NUMBER ’ + ’1’
4.2.2.5 CONTAINS
In DB2, the CONTAINS function searches a text search index using criteria that are specified in a search
argument and returns a result of bit type about whether or not a match was found.
DB2 Example:
CONTAINS (PEOPLE.FIRST_NAME, ’THOM%’)
Guide to Migrating from DB2 to SQL Server and Azure SQL DB Page | 142
Solution:
In SQL Server, use CONTAINS function. It is returning logical true or false, so you are need to wrap function
to get bit result.
In DB2, returns a value that is the result of decrypting encrypted data using a password string.
DB2 Example:
DECRYPT_BIN(@STR, @PASS)
DECRYPT_CHAR(@STR, @PASS)
Solution:
In SQL Server, use DECRYPTBYPASSPHRASE function which decrypts data that was encrypted with a
passphrase.
In DB2, returns a value that is the result of encrypting a data string expression.
The ENCRYPT_TDES function uses the Triple DES encryption algorithm.
DB2 Example:
ENCRYPT(@STR)
ENCRYPT(@STR, @PASS)
ENCRYPT(@STR, @PASS, @HINT)
ENCRYPT_TDES(@STR)
ENCRYPT_TDES(@STR, @PASS)
ENCRYPT_TDES(@STR, @PASS, @HINT)
Solution:
In SQL Server, use ENCRYPTBYPASSPHRASE function, which encrypt data with a passphrase using the
TRIPLE DES algorithm with a 128 key bit length.
Hint is ignored.
Guide to Migrating from DB2 to SQL Server and Azure SQL DB Page | 143
4.2.2.8 GENERATE_UNIQUE
In DB2, returns a bit data character string that is unique compared to any other execution of the same
function. The result of this function can be used to provide unique values in a table. Each successive value
will be greater than the previous value, providing a sequence that can be used within a table. The sequence
is based on the time when the function was executed.
DB2 Example:
GENERATE_UNIQUE()
Solution:
In SQL Server, use GETDATE function and SEQUENCE to generate unique value in necessary format.
The limitation is method does not work in function context because of SEQUENCE.
4.2.2.9 INITCAP
In DB2, returns a string with the first character of each word converted to uppercase and the rest to
lowercase.
DB2 Example:
INITCAP(’JOHN SMIT’)
Solution:
In SQL Server, create such function:
CREATE FUNCTION DBO.INITCAP(@S NVARCHAR(MAX)) RETURNS NVARCHAR(MAX)
BEGIN
DECLARE
@RESULT NVARCHAR(MAX) = ’’,
@MODE INT = 0,
@I INT = 1,
@CUR INT,
@CHAR NVARCHAR(1),
@LEN INT = LEN(@S),
WHILE @I <= @LEN
BEGIN
SET @CHAR = SUBSTRING(@S, @I, 1);
SET @CUR = ISNUMERIC(@CHAR) +
IIF(UNICODE(UPPER(@CHAR)) != UNICODE(LOWER(@CHAR)), 1, 0);
SET @RESULT = @RESULT +
IIF(@MODE != @CUR, UPPER(@CHAR), LOWER(@CHAR));
SET @MODE = @CUR;
SET @I = @I + 1;
Guide to Migrating from DB2 to SQL Server and Azure SQL DB Page | 144
END
RETURN @RESULT;
END
In DB2, returns a string, where LENGTH bytes are deleted from SOURCE beginning at START,
and INSERT is inserted into SOURCE beginning at START.
DB2 Example:
INSERT(@SOURCE_STR, @START, @LENGTH, @INSERT_STR)
OVERLAY(@SOURCE_STR, @INSERT_STR, @START, @LENGTH)
OVERLAY(@SOURCE_STR PLACING @INSERT_STR FROM @START FOR @LENGTH)
Solution:
In SQL Server, use STUFF function.
DB2 Example:
INSTR(@STR, @FRAGMENT, @START, @OCCURED)
INSTRB(@STR, @FRAGMENT, @START)
INSTR2(@STR, @FRAGMENT)
Solution:
In SQL Server, use such recursive solution:
CREATE FUNCTION DBO.INSTR(@S NVARCHAR(MAX), @FRAGMENT NVARCHAR(MAX),
@START BIGINT, @OCCURED INT = NULL, @CODEUNITS VARCHAR(20) = NULL)
RETURNS BIGINT AS
BEGIN
IF @ OCCURED IS NULL OR @ OCCURED < 1 SET @OCCURED = 1;
IF @START < 1 SET @START = 1;
Guide to Migrating from DB2 to SQL Server and Azure SQL DB Page | 145
SQL Server Example:
DBO.INSTR(@STR, @FRAGMENT, @START, @OCCURED)
DBO.INSTR(@STR, @FRAGMENT, @START, DEFAULT)
DBO.INSTR(@STR, @FRAGMENT, DEFAULT, DEFAULT)
4.2.2.12 LCASE
In DB2, returns a string in which all the SBCS characters have been converted to lowercase characters.
DB2 Example:
LCASE(@S)
Solution:
In SQL Server, use LOWER function.
In DB2, returns the starting position of one string within another string.
DB2 Example:
LOCATE(@FRAGMENT, @STR, @START)
POSITION(@FRAGMENT, @STR)
Solution:
In SQL Server, use CHARINDEXfunction.
4.2.2.14 LPAD
In DB2, returns a string that is padded on the left with the specified character, or with blanks.
DB2 Example:
LPAD(’123’, 10, ’0’)
Solution:
In SQL Server, use RIGHT function.
Guide to Migrating from DB2 to SQL Server and Azure SQL DB Page | 146
4.2.2.15 OCTET_LENGTH
DB2 Example:
OCTET_LENGTH(@STR)
Solution:
In SQL Server, use function DATALENGTH which returns the number of bytes used to represent any
expression.
4.2.2.16 REPEAT
DB2 Example:
REPEAT(@STR, @COUNT)
Solution:
In SQL Server, use REPLICATE function.
4.2.2.17 RPAD
In DB2, returns a string that is padded on the right with the specified character, string, or with blanks.
DB2 Example:
RPAD(’F’, 10, ’O’)
Solution:
In SQL Server, use LEFT function.
4.2.2.18 SPACE
Guide to Migrating from DB2 to SQL Server and Azure SQL DB Page | 147
DB2 Example:
SPACE(N)
Solution:
In SQL Server, use REPLICATE function.
DB2 Example:
STRIP(:S)
STRIP(:S, BOTH)
STRIP(:STR, LEADING, ’0’)
TRIM(:S)
TRIM(:S, BOTH)
TRIM(:STR, LEADING, ’0’)
Solution:
In SQL Server, create special function or use inline T-SQL code with cycles.
DB2 Example:
SUBSTR (:STR, :START, :LEN)
SUBSTR (:STR, :START)
Solution:
In SQL Server, use SUBSTRING function.
If nessesary negative LEN must be exchanged to 1.
Guide to Migrating from DB2 to SQL Server and Azure SQL DB Page | 148
4.2.2.21 UCASE
Solution:
In SQL Server, use UPPER function.
DB2 Example:
TRUNCATE(:DATETIME_VALUE, ’HH’)
Solution:
In SQL Server, use DATEPART function to decompose argument to parts, then use DATEFROMPARTS or
DATETIMEFROMPARTS to compose parts into truncated value.
4.2.3.2 ATAN2
In DB2, returns the arc tangent of x and y coordinates as an angle expressed in radians.
DB2 Example:
ATAN2(:X, :Y)
Solution:
In SQL Server, use ATAN function with precalculated parameter.
4.2.3.3 ATANH
DB2 Example:
ATANH(:x)
Guide to Migrating from DB2 to SQL Server and Azure SQL DB Page | 149
Solution:
In SQL Server, use formula for arc tangent.
SQL Server Example:
LOG((1 + @X) / (1 - @X)) / 2
4.2.3.4 COMPARE_DECFLOAT
In DB2, returns a SMALLINT value that indicates whether the two arguments are eq
ual or unordered, or whether one argument is greater than the other.
DB2 Example:
COMPARE_DECFLOAT(X, Y)
Solution:
In SQL Server, the function is converted to inline expression.
DB2 Example:
DECFLOAT_FORMAT(:X)
Solution:
In SQL Server, instead DECFLOAT use similar DECIMAL(34, 10) data type.
Before casting clear argument from characters which enabled in DB2 but wrong in SQL Server number
presentation.
4.2.3.6 DECFLOAT_SORTKEY
The DECFLOAT_SORTKEY function returns a binary value that can be used when sorting DECFLOAT
values. The sorting occurs in a manner that is consistent with the IEEE 754R specification on total ordering.
Guide to Migrating from DB2 to SQL Server and Azure SQL DB Page | 150
DB2 Example:
DECFLOAT_SORTKEY(X)
Solution:
SQL Server, numbers don’t have to be converted for comparison.
4.2.3.7 DIGITS
DB2 Example:
DIGITS(:X)
Solution:
In SQL Server, remove decimal point and add leading zeros to make length according to data type
precission.
4.2.3.8 MOD
In DB2, returns the remainder of the first argument divided by the second argument.
DB2 Example:
MOD(:X, :Y)
Solution:
In SQL Server, use % operator, which returns the remainder of one number divided by another.
Guide to Migrating from DB2 to SQL Server and Azure SQL DB Page | 151
4.2.3.9 MULTIPLY_ALT
In DB2, returns the product of two arguments as a decimal value. This function is useful when the sum of the
argument precisions is greater than 31.
DB2 Example:
MULTIPLAY_ALT(:X, :Y)
Solution:
In SQL Server, use * operator.
4.2.3.10 NORMALIZE_DECFLOAT
In DB2, returns a decimal floating-point value that is the result of the argument set to its simplest form.
DB2 Example:
NORMALIZE_DECFLOAT(:X)
Solution:
In SQL Server, use casting to REAL.
4.2.3.11 QUANTIZE
In DB2, returns a decimal floating-point number that is equal in value and sign to the first argument, and
whose exponent is equal to the
exponent of the second argument.
Returns @val with the same precission (in fact) as @exp.
By other words, with the same count of significant digits.
DB2 Example:
SELECT QUANTIZE(:X, :EXP) FROM SYSIBM.SYSDUMMY1;
Solution:
In SQL Server, emulate necessary behavior by T-SQL code. You can wrap it into custom defined scalar
function too.
4.2.3.12 TANH
DB2 Example:
TANH(:X)
Solution:
In SQL Server, use hyperbolic tangent formula.
4.2.3.13 TOTALORDER
Solution:
In SQL Server, use IIF function.
DB2 Example:
BITAND(:X, :Y)
Solution:
In SQL Server, use & operator.
Guide to Migrating from DB2 to SQL Server and Azure SQL DB Page | 153
SQL Server Example:
@X & @Y
4.2.4.2 BITANDNOT
In DB2, clears any bit in the first argument that is in the second argument.
DB2 Example:
BITANDNOT(:X, :Y)
Solution:
In SQL Server, use & and ~ operators.
4.2.4.3 BITNOT
DB2 Example:
BITNOT(:X)
Solution:
In SQL Server, use ~ operator.
4.2.4.4 BITOR
DB2 Example:
BITOR(:X, :Y)
Solution:
In SQL Server, use | operator.
Guide to Migrating from DB2 to SQL Server and Azure SQL DB Page | 154
4.2.4.5 BITXOR
DB2 Example:
BITXOR(:X, :Y)
Solution:
In SQL Server, use ^ operator.
4.2.4.6 DECODE
DB2 Example:
DECODE(:X, :Y1, :Z1, :Y2, :Z2, :Z)
Solution:
In SQL Server, use IIF or CASE function.
DB2 Example:
GREATEST(:X1, :X2, :X3, :X4, :X5)
Solution:
In SQL Server, use MAX aggregate function.
Guide to Migrating from DB2 to SQL Server and Azure SQL DB Page | 155
SQL Server Example:
SELECT MAX(C) FROM (VALUES (@X1), (@X2), (@X3), (@X4), (@X5)) T(C)
4.2.4.8 HEX
DB2 Example:
HEX(:X)
Solution:
In SQL Server, use casting to VARBINARY, then converting to VARCHAR.
4.2.4.9 IDENTITY_VAL_LOCAL
In DB2, returns the most recently assigned value for an identity column.
Solution:
In SQL Server, use SCOPE_IDENTITY function.
DB2 Example:
IFNULL(:X, :Y)
Solution:
In SQL Server, use COALESCE function.
DB2 Example:
LEAST(:X1, :X2, :X3, :X4, :X5)
Guide to Migrating from DB2 to SQL Server and Azure SQL DB Page | 156
Solution:
In SQL Server, use MIN aggregate function.
4.2.4.12 NVL2
In DB2, returns the second argument when the first argument is not NULL. If the first argument is NULL, the
third argument is returned.
DB2 Example:
NVL2(:TEST, :THEN, :ELSE)
Solution:
In SQL Server, use IIF function.
In DB2, returns a datetime value that represents expression plus a specified number of months.
DB2 Example:
ADD_MONTHS(:X, :QTY)
Solution:
In SQL Server, use function DATEADD which returns a specified date with the specified number interval
(signed integer) added to a specified datepart of that date.
4.2.5.2 DAYNAME
In DB2, returns a character string containing the name of the day (for example, Friday) for the day portion of
expression, based on locale-name or the value of the special register CURRENT LOCALE LC_TIME.
Guide to Migrating from DB2 to SQL Server and Azure SQL DB Page | 157
DB2 Example:
DAYNAME(@X)
Solution:
In SQL Server, use function DATENAME which returns a character string that represents the
specified datepart of the specified date
4.2.5.3 DAYOFMONTH
The DAYOFMONTH function returns the day part of a value. The function is similar to the DAY function,
except DAYOFMONTH does not support a date or timestamp duration as an argument.
DB2 Example:
DAYOFMONTH(@X)
Solution:
In SQL Server, use function DAY which returns an integer representing the day (day of the month) of the
specified date.
4.2.5.4 DAYOFWEEK
In DB2, returns the day of the week from a value, where 1 is Sunday and 7 is Saturday.
DB2 Example:
DAYOFWEEK(@X)
Solution:
In SQL Server, use tricky formula from the example.
4.2.5.5 DAYOFWEEK_ISO
In DB2, returns the day of the week from a value, where 1 is Monday and 7 is Sunday.
DB2 Example:
DAYOFWEEK_ISO(:X)
Guide to Migrating from DB2 to SQL Server and Azure SQL DB Page | 158
Solution:
In SQL Server, use tricky formula from the example.
4.2.5.6 DAYOFYEAR
DB2 Example:
DAYOFYEAR(:X)
Solution:
In SQL Server, use DATEPART function.
4.2.5.7 DAYS
DB2 Example:
DAYS(:X)
Solution:
In SQL Server, use DATEDIFF function with the birth of Christ as first argument.
4.2.5.8 EXTRACT
DB2 Example:
EXTRACT(YEAR FROM :X)
Solution:
In SQL Server, use DATEPART function.
Guide to Migrating from DB2 to SQL Server and Azure SQL DB Page | 159
4.2.5.9 HOUR
DB2 Example:
HOUR(:X)
Solution:
In SQL Server, use DATEPART function.
4.2.5.10 JULIAN_DAY
In DB2, returns an integer value representing the number of days from January 1, 4712 B.C. to the date
specified in the argument.
DB2 Example:
JULIAN_DAY(:X)
Solution:
In SQL Server, use DATEDIFF function with the birth of Christ as first argument plus 1721426 as difference
between calendars.
4.2.5.11 LAST_DAY
In DB2, returns a datetime value that represents the last day of the month of the argument.
DB2 Example:
LAST_DAY(EXPR)
Solution:
In SQL Server, use EOMONTH function.
4.2.5.12 MICROSECOND
Guide to Migrating from DB2 to SQL Server and Azure SQL DB Page | 160
DB2 Example:
MICROSECOND(:X)
Solution:
In SQL Server, use DATEPART function.
4.2.5.13 MIDNIGHT_SECONDS
In DB2, returns an integer value representing the number of seconds between midnight
and a specified time value.
DB2 Example:
MIDNIGHT_SECOND(:X)
Solution:
In SQL Server, calculate number of seconds since midnight using DATEDIFF.
4.2.5.14 MINUTE
DB2 Example:
MINUTE(:X)
Solution:
In SQL Server, use DATEPART function.
4.2.5.15 MONTHNAME
In DB2, returns a character string containing the name of the month (for example, January) for the month
portion of expression.
DB2 Example:
MONTHNAME(:X)
Guide to Migrating from DB2 to SQL Server and Azure SQL DB Page | 161
Solution:
In SQL Server, use function DATENAME which returns a character string that represents the
specified datepart of the specified date
4.2.5.16 MONTHS_BETWEEN
Returns an estimate of the number of months between ARGUMENT #1 and ARGUMENT #2.
DB2 Example:
MONTHS_BETWEEN(:ARG1, :ARG2)
Solution:
In SQL Server, use DATEDIFF function, but note that strictly speaking it is no equivalent to DB2 result.
4.2.5.17 QUARTER
In DB2, returns an integer that represents the quarter of the year in which a date resides.
DB2 Example:
QUARTER(:X)
Solution:
In SQL Server, use DATEPART function.
4.2.5.18 SECOND
DB2 Example:
SECOND(:X)
Solution:
In SQL Server, use formula.
Guide to Migrating from DB2 to SQL Server and Azure SQL DB Page | 162
SQL Server Example:
DATEPART(SECOND, @X) +
ROUND(DATEPART(NANOSECOND, @DATE) / 1000000000.000000000 , @SCALE, 12)
In DB2, returns a timestamp value based on a date, time, or timestamp argument. If the argument is a date, it
inserts zero for all the time elements. If the argument is a time, it inserts the value of CURRENT DATE for the
date elements, and zero for the fractional time element.
DB2 Example:
TIMESTAMP_ISO(:X)
Solution:
In SQL Server, use convertion into DATETIME2 data type.
4.2.5.20 WEEK
In DB2, returns the week of the year from a value, where the week starts with Sunday.
DB2 Example:
WEEK(:X)
Solution:
In SQL Server, use xxxxx function.
4.2.5.21 WEEK_ISO
In DB2, returns the week of the year from a value, where the week starts with Monday.
DB2 Example:
WEEK_ISO(:X)
Solution:
In SQL Server, use DATEPART function.
Guide to Migrating from DB2 to SQL Server and Azure SQL DB Page | 163
4.2.6 Casting Functions
4.2.6.1 BIGINT
DB2 Example:
BIGINT(:X)
Solution:
In SQL Server, use castint to BIGINT. Different data types are converted by different way. See examples.
In DB2, the BINARY function returns a BINARY (fixed-length binary string) representation of a string of any
type or of a row ID type.
DB2 Example:
BINARY(:STR)
VARBINARY(:STR, LEN)
Solution:
In SQL Server, use casting to VARBINARY.
In DB2, the CLOB function returns a CLOB representation of a character string type. In a Unicode database, if a
supplied argument is a graphic string, it is first converted to a character string data type before the function is
executed.
Guide to Migrating from DB2 to SQL Server and Azure SQL DB Page | 164
DB2 Example:
CLOB(:STR)
TO_CLOB(:STR, :LEN)
Solution:
In SQL Server, use casting to VARCHAR.
4.2.6.4 DATE
DB2 Example:
DATE ('2015-10-20')
Solution:
In SQL Server, use different ways for different dada types. See examples.
4.2.6.5 DBCLOB
DB2 Example:
DBCLOB(:STR)
DBCLOB(:STR, :LEN)
Solution:
In SQL Server, use casting to VARCHAR.
DB2 Example:
DECFLOAT(:STR)
DECFLOAT(:STR, :DEC)
Solution:
In SQL Server, use casting into decimal(38, 10).
DB2 Example:
DECIMAL(:X, :SCALE, :DEC)
DECIMAL(:X, :SCALE)
DECIMAL(:X)
Solution:
In SQL Server, use casting into decimal(31, 10) after cleaning argument from wrong characters.
Guide to Migrating from DB2 to SQL Server and Azure SQL DB Page | 166
4.2.6.8 DOUBLE_PRECISION, DOUBLE
DB2 Example:
DOUBLE_PRECISION(:X)
Solution:
In SQL Server, use casting into DOUBLE PRECISSION.
4.2.6.9 EMPTY_BLOB
DB2 Example:
EMPTY_BLOB
Solution:
In SQL Server, use VARBINARY of zero length.
Solution:
In SQL Server, use empty string
4.2.6.11 FLOAT
DB2 Example:
FLOAT(:X)
Guide to Migrating from DB2 to SQL Server and Azure SQL DB Page | 167
Solution:
In SQL Server, use casting to FLOAT(37).
DB2 Example:
INTEGER(:x)
Solution:
In SQL Server, use castint to INT. Different data types are converted by different way. See examples.
DB2 Example:
NCLOB(:STR)
NCLOB(:STR, :LEN)
Solution:
In SQL Server, instead NCLOB the VASRCHAR is used.
4.2.6.14 REAL
Guide to Migrating from DB2 to SQL Server and Azure SQL DB Page | 168
DB2 Example:
REAL(:X)
Solution:
In SQL Server, use casting in REAL.
4.2.6.15 SMALLINT
Solution:
In SQL Server, use casting into SMALLINT.
4.2.6.16 TIME
Solution:
In SQL Server, use xxxxx function.
4.2.6.17 TIMESTAMP
DB2 Example:
TIMESTAMP(X)
Solution:
In SQL Server, use xxxxx function.
Guide to Migrating from DB2 to SQL Server and Azure SQL DB Page | 169
SQL Server Example for strings:
IIF(LENGTH(@X) = 13,
CONVERT(DATETIME, SUBSTRING(CAST(@X AS VARCHAR(MAX)), 1, 8), 112) +
CAST(SUBSTRING(@X), 9, 2) + ':' +
SUBSTRING(@X), 11, 2) + '.' +
SUBSTRING(@X), 13, 1) AS DATETIME),
CAST(@X AS DATETIME2))
In DB2, aggregates a set string elements into one string by concatenating the strings.
DB2 Example:
SELECT LISTAGG(COL1) FROM TABLE1
Solution:
In SQL Server, use FOR XML clause.
4.2.7.2 MEDIAN
DB2 Example:
SELECT MEDIAN(SALARY)
FROM EMPLOYEE
WHERE WORKDEPT = ‘D11’
Solution:
In SQL Server, emulate median evaluating through SQL queries.
Guide to Migrating from DB2 to SQL Server and Azure SQL DB Page | 170
4.2.7.3 STDDEV, STDDEV_SAMP
DB2 Example:
STDDEV(DISTINCT COL1)
Solution:
In SQL Server, use STDEVP function which returns the statistical standard deviation for the population for all
values in the specified expression.
DB2 Example:
VARIANCE(COL1)
Solution:
In SQL Server, use function VARP which returns the statistical variance for the population for all values in the
specified expression.
Guide to Migrating from DB2 to SQL Server and Azure SQL DB Page | 171
5.0 Data Migration
Data migration is the process of transferring data from a source database to a target database. Both
SQL Server and DB2 provide built-in utilities for data migration.
DB2 provides the EXPORT utility for exporting data from DB2 to flat (text) files. For information about DB2
EXPORT, go to
pic.dhe.ibm.com/infocenter/db2luw/v9r7/index.jsp?topic=%2Fcom.ibm.db2.luw.sql.ref.doc%2Fdoc%2Fr00594
82.html.
SQL Server offers the Bulk Copy Program (bcp), the BULK INSERT statement, and the SQL Server
Integration Services (SSIS) platform of utilities for importing data. These utilities can be used to perform data
migration from DB2 to SQL Server.
Bulk Copy Program (bcp) is a command-line utility that uses the ODBC bulk copy API. For bcp, data
must be input as ASCII text files. For more information about BCP, go to msdn.microsoft.com/en-
us/library/ms162802.aspx.
BULK INSERT is a T-SQL statement that has same functionality as bcp, and also imports DB2 data as
flat files. The major difference from bcp is that BULK INSERT is available from within a database session.
For more information about BULK INSERT, go to msdn.microsoft.com
/en-us/library/ms188365.aspx.
SQL Server Integration Services (SSIS) is a powerful set of tools that can be used to extract, transform,
and load DB2 data to SQL Server databases. SSIS wizards simplify the process of defining and
performing the import. SSIS also provides access to the BULK INSERT statement in the BULK INSERT
task. SSIS offers two methods for importing:
Load data from flat files.
Provide direct connectivity with DB2 using OLEDB AND ODBC providers to extract data from
DB2.
This section details the steps to perform while migrating the data, in these three areas:
Pre-implementation
Implementation
Post-implementation
The various SQL Server options for migrating data from DB2 to SQL Server are presented in section 5.2,
“Implementation Tasks.”
Note that Azure SQL DB doesn’t support working with the file system.
5. In SQL Server, disable triggers on all tables into which data will be loaded using the command below:
ALTER TABLE <TABLENAME> DISABLE TRIGGER ALL
Guide to Migrating from DB2 to SQL Server and Azure SQL DB Page | 172
In the BULK INSERT statement, use the FIRE_TRIGGERS option to enable and disable triggers.
6. In SQL Server, handle identity column inserts:
In bcp, use the –E switch.
In BULK INSERT, use the KEEPIDENTITY argument.
Guide to Migrating from DB2 to SQL Server and Azure SQL DB Page | 173
5.2.2 Two-Step Process Using the bcp Utility
The bcp utility is a command-line utility that can be used to load data to SQL Server from flat files.
Syntax
BCP DBNAME.SCHEMA.TABLENAME IN DATA_FILE –T FIELD_TERMINATOR –S
SERVER_NAME -U LOGIN_ID –P PASSWORD
where -t specifies the field delimiter.
To make the bcp process repeatable, create a format file and use the -f switch to specify the format file for
future imports.
The following arguments should be taken into consideration while importing large volume of data from DB2.
-e error_file logs errors to an error file during the import procedure.
-b batch_size specifies the size of the batch; the default is all the rows. Each batch is imported and
logged in a separate transaction. After a given transaction is committed, the rows that have been
imported by that transaction are committed. If the operation fails, only those rows that have been
imported from the current batch are rolled back, and you can resume importing data starting at the
beginning of the failed batch, rather than at the beginning of the data file.
-E specifies that the identity value or values in the imported data file are to be used for the identity
column. If -E is not specified, then the identity values for this column in the data file being imported are
ignored, and SQL Server automatically assigns unique values based on the seed and increment values
that were specified during table creation.
Note that Azure SQL DB doesn’t support working with the file system.
To import from a flat file using the SSIS Import and Export Wizard
1. From SQL Server Management Studio, launch the SSIS Import and Export Wizard: right-click the target
SQL Server database, point to Tasks, and then click Import Data.
2. On the Welcome page, click Next.
3. On the Choose a Data Source page, do the following:
Guide to Migrating from DB2 to SQL Server and Azure SQL DB Page | 174
In the Data Source list, select Flat File Source.
In the File name box, enter the name of the file that contains the DB2 table data.
Move to the Columns subpage, and then choose the appropriate column delimiter and row delimiter.
4. On the Choose a Destination page, in the Destination box, select Microsoft OLEDB for SQL Server
and provide server and login information for the destination SQL Server database.
5. On the Save and Run Package page, click Finish to run the import package immediately, or click Save
to run the package at later time or to do customization.
Guide to Migrating from DB2 to SQL Server and Azure SQL DB Page | 175
Divide the data to be imported among clients into same number of data files as there are clients.
Place one of file for each of the clients.
To use the processor most effectively, distribute data evenly among the clients and make sure that all
the data files are approximately the same size.
Make sure that indexes do not exist for any table, because if indexes exist on a table, you cannot
perform a parallel load operation by using the TABLOCK hint.
Use TABLOCK to avoid row-at-a-time locking. Table locking can improve the performance of the
bulk-import operation by reducing lock contention on the table.
Use minimal logging by setting the database recovery model to Bulk-logged or Simple.
Make the batch size as large as practical. Typically, the larger the batch size, the better the
performance of the bulk-import operation. To achieve maximum performance, the batch size
specified for each client should be the same as the size of the client’s data file.
Disable triggers in target tables.
Disable constraints. By default, constraints are ignored when bcp or BULK INSERT is used.
Guide to Migrating from DB2 to SQL Server and Azure SQL DB Page | 176
6.0 Terminology Mapping
Table 6-1 shows the terminology mapping of physical objects between DB2 UDB and SQL Server.
Table 6-2 shows the terminology mapping of logical objects between DB2 UDB and SQL Server.
Table 6-3 shows the terminology mapping of database objects between DB2 UDB and SQL Server.
Table 6-4 shows the terminology mapping of administration and usage between DB2 UDB and SQL Server.
Guide to Migrating from DB2 to SQL Server and Azure SQL DB Page | 178
7.0 Conclusion
From this migration guide you learned the differences between DB2 and SQL Server 2014 database
platforms, and the steps necessary to convert a DB2 database to SQL Server.
It explains the algorithms that SSMA for DB2 uses to perform this conversion so that you can better
understand the processes that are executed when you run the SSMA Convert Schema and Migrate Data
commands. For those cases when SSMA does not handle a particular migration issue, approaches to manual
conversion are included.
Did this paper help you? Please give us your feedback. Tell us on a scale of 1 (poor) to 5
(excellent), how would you rate this paper and why have you given it this rating? For example:
Are you rating it high due to having good examples, excellent screenshots, clear writing, or
another reason?
Are you rating it low due to poor examples, fuzzy screenshots, unclear writing?
This feedback will help us improve the quality of the white papers we release.
Send feedback.
Guide to Migrating from DB2 to SQL Server and Azure SQL DB Page | 179