1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 373 374 375 376 377 378 379 380 381 382 383 384 385 386 387 388 389 390 391 392 393 394 395 396 397 398 399 400 401 402 403 404 405 406 407 408 409 410 411 412 413 414 415 416 417 418 419 420 421 422 423 424 425 426 427 428 429 430 431 432 433 434 435 436 437 438 439 440 441 442 443 444 445 446 447 448 449 450 451 452 453 454 455 456 457 458 459 460 461 462 463 464 465 466 467 468 469 470 471 472 473 474 475 476 477 478 479 480 481 482 483 484 485 486 487 488 489 490 491 492 493 494 495 496 497 498 499 500 501 502 503 504 505 506 507 508 509 510 511 512 513 514 515 516 517 518 519 520 521 522 523 524 525 526 527 528 529 530 531 532 533 534 535 536 537 538 539 540 541 542 543 544 545 546 547 548 549 550 551 552 553 554 555 556 557 558 559 560 561 562 563 564 565 566 567 568 569 570 571 572 573 574 575 576 577 578 579 580 581 582 583 584 585 586 587 588 589 590 591 592 593 594 595 596 597 598 599 600 601 602 603 604 605 606 607 608 609 610 611 612 613 614 615 616 617 618 619 620 621 622 623 624 625 626 627 628 629 630 631 632 633 634 635 636 637 638 639 640 641 642 643 644 645 646 647 648 649 650 651 652 653 654 655 656 657 658 659 660 661 662 663 664 665 666 667 668 669 670 671 672 673 674
|
<?xml version="1.0" encoding="UTF-8"?>
<!-- $Id: sqlissues.xml,v 1.20 2005/07/01 17:06:32 fredt Exp $ -->
<chapter id="sql_issues-chapter">
<title id="sql_issues-title">SQL Issues</title>
<chapterinfo>
<authorgroup>
<author>
<firstname>Fred</firstname>
<surname>Toussi</surname>
<affiliation>
<orgname>HSQLDB Development Group</orgname>
</affiliation>
<email>ft@cluedup.com</email>
</author>
</authorgroup>
<edition>$Revision: 1.20 $</edition>
<pubdate>$Date: 2005/07/01 17:06:32 $</pubdate>
<keywordset>
<keyword>Hsqldb</keyword>
<keyword>SQL</keyword>
</keywordset>
<legalnotice>
<para>Copyright 2002-2005 Fred Toussi. Permission is granted to
distribute this document without any alteration under the terms of the
HSQLDB license. Additional permission is granted to the HSQLDB
Development Group to distribute this document with or without
alterations under the terms of the HSQLDB license.</para>
</legalnotice>
</chapterinfo>
<section>
<title>Purpose</title>
<para>Many questions repeatedly asked in Forums and mailing lists are
answered in this guide. If you want to use HSQLDB with your application,
you should read this guide.</para>
</section>
<section>
<title>SQL Standard Support</title>
<para>HSQLDB 1.8.0 supports the dialect of SQL defined by SQL standards
92, 99 and 2003. This means where a feature of the standard is supported,
e.g. left outer join, the syntax is that specified by the standard text.
Many features of SQL92 and 99 up to Advanced Level are supported and there
is support for most of SQL 2003 Foundation and several optional features
of this standard. However, certain features of the Standards are not
supported so no claim is made for full support of any level of the
standards.</para>
<para>The SQL Syntax chapter of this guide <link endterm="sqlsyntax-title"
linkend="sqlsyntax-chapter" /> lists all the keywords and syntax that is
supported. When writing or converting existing SQL DDL (Data Definition
Language) and DML (Data Manipulation Language) statements for HSQLDB, you
should consult the supported syntax and modify the statements
accordingly.</para>
<para>Several words are reserved by the standard and cannot be used as
table or column names. For example, the word POSITION is reserved as it is
a function defined by the Standards with a similar role as
String.indexOf() in Java. HSQLDB does not currently prevent you from using
a reserved word if it does not support its use or can distinguish it. For
example BEGIN is a reserved words that is not currently supported by
HSQLDB and is allowed as a table or column name. You should avoid the use
of such words as future versions of HSQLDB are likely to support the words
and will reject your table definitions or queries. The full list of SQL
reserved words is in the source of the
<classname>org.hsqldb.Token</classname> class.</para>
<para>HSQLDB also supports some keywords and expressions that are not part
of the SQL standard as enhancements. Expressions such as <literal>SELECT
TOP 5 FROM ..</literal>, <literal>SELECT LIMIT 0 10 FROM ...</literal> or
<literal>DROP TABLE mytable IF EXISTS</literal> are among such
constructs.</para>
<para>All keywords, can be used for database objects if they are double
quoted.</para>
</section>
<section>
<title>Constraints and Indexes</title>
<section>
<title>Primary Key Constraints</title>
<para>Before 1.7.0, a<literal> CONSTRAINT <name> PRIMARY
KEY</literal> was translated internally to a unique index and, in
addition, a hidden column was added to the table with an extra unique
index. From 1.7.0 both single-column and multi-column PRIMARY KEY
constraints are supported. They are supported by a unique index on the
primary key column(s) specified and no extra hidden column is maintained
for these indexes.</para>
</section>
<section>
<title>Unique Constraints</title>
<para>According to the SQL standards, a unique constraint on a single
column means no two values are equal unless one of them is NULL. This
means you can have one or more rows where the column value is
NULL.</para>
<para>A unique constraint on multiple columns (c1, c2, c3, ..) means
that no two sets of values for the columns are equal unless at lease one
of them is NULL. Each single column taken by itself can have repeat
values. The following example satisfies a UNIQUE constraint on the two
columns:</para>
<example>
<title>Column values which satisfy a 2-column UNIQUE
constraint</title>
<simplelist columns="2" type="horiz">
<member>1,</member>
<member>2</member>
<member>2,</member>
<member>1</member>
<member>2,</member>
<member>2</member>
<member>NULL,</member>
<member>1</member>
<member>NULL,</member>
<member>1</member>
<member>1,</member>
<member>NULL</member>
<member>NULL,</member>
<member>NULL</member>
<member>NULL,</member>
<member>NULL</member>
</simplelist>
</example>
<para>Since version 1.7.2 the behaviour of UNIQUE constraints and
indexes with respect to NULL values has changed to conform to SQL
standards. A row, in which the value for any of the UNIQUE constraint
columns is NULL, can always be added to the table. So multiple rows can
contain the same values for the UNIQUE columns if one of the values is
NULL.</para>
</section>
<section>
<title>Unique Indexes</title>
<para>In 1.8.0, user defined UNIQUE indexes can still be declared but
they are deprecated. You should use a UNIQUE constraint instead.</para>
<para><literal>CONSTRAINT <name> UNIQUE</literal> always creates
internally a unique index on the columns, as with previous versions, so
it has exactly the same effect as the deprecated UNIQUE index
declaration.</para>
</section>
<section>
<title>FOREIGN KEYS</title>
<para>From version 1.7.0, HSQLDB features single and multiple column
foreign keys. A foreign key can also be specified to reference a target
table without naming the target column(s). In this case the primary key
column(s) of the target table is used as the referenced column(s). Each
pair of referencing and referenced columns in any foreign key should be
of identical type. When a foreign key is declared, a unique constraint
(or primary key) must exist on the referenced columns in the primary key
table. A non-unique index is automatically created on the referencing
columns. For example:</para>
<informalexample>
<programlisting>
CREATE TABLE child(c1 INTEGER, c2 VARCHAR, FOREIGN KEY (c1, c2) REFERENCES parent(p1, p2));</programlisting>
</informalexample>
<para>There must be a UNIQUE constraint on columns
<literal>(p1,p2)</literal> in the table named "parent". A non-unique
index is automatically created on columns <literal>(c1, c2)</literal> in
the table named "child". Columns <literal>p1</literal> and
<literal>c1</literal> must be of the same type (INTEGER). Columns
<literal>p2</literal> and <literal>c2</literal> must be of the same type
(VARCHAR).</para>
</section>
<section>
<title>Indexes and Query Speed</title>
<para>HSQLDB does not use indexes to improve sorting of query results.
But indexes have a crucial role in improving query speed. If no index is
used in a query on a single table, such as a DELETE query, then all the
rows of the table must be examined. With an index on one of the columns
that is in the WHERE clause, it is often possible to start directly from
the first candidate row and reduce the number of rows that are
examined.</para>
<para>Indexes are even more important in joins between multiple tables.
<literal>SELECT ... FROM t1 JOIN t2 ON t1.c1 = t2.c2 </literal> is
performed by taking rows of t1 one by one and finding a matching row in
t2. If there is no index index on t2.c2 then for each row of t1, all the
rows of t2 must be checked. Whereas with an index, a matching row can be
found in a fraction of the time. If the query also has a condition on
t1, e.g., <literal>SELECT ... FROM t1 JOIN t2 ON t1.c1 = t2.c2 WHERE
t1.c3 = 4</literal> then an index on t1.c3 would eliminate the need for
checking all the rows of t1 one by one, and will reduce query time to
less than a millisecond per returned row. So if t1 and t2 each contain
10,000 rows, the query without indexes involves checking 100,000,000 row
combinations. With an index on t2.c2, this is reduced to 10,000 row
checks and index lookups. With the additional index on t2.c2, only about
4 rows are checked to get the first result row.</para>
<para>Indexes are automatically created for primary key and unique
columns. Otherwise you should define an index using the CREATE INDEX
command.</para>
<para>Note that in HSQLDB a unique index on multiple columns can be used
internally as a non-unique index on the first column in the list. For
example: <literal>CONSTRAINT name1 UNIQUE (c1, c2, c3); </literal> means
there is the equivalent of <literal>CREATE INDEX name2 ON
atable(c1);</literal>. So you do not need to specify an extra index if
you require one on the first column of the list.</para>
<para>In 1.8.0, a multi-column index will speed up queries that contain
joins or values on ALL the columns. You need NOT declare additional
individual indexes on those columns unless you use queries that search
only on a subset of the columns. For example, rows of a table that has a
PRIMARY KEY or UNIQUE constraint on three columns or simply an ordinary
index on those columns can be found efficiently when values for all
three columns are specified in the WHERE clause. For example,
<literal>SELECT ... FROM t1 WHERE t1.c1 = 4 AND t1.c2 = 6 AND t1.c3 = 8
</literal>will use an index on <literal>t1(c1,c2,c3)</literal> if it
exists.</para>
<para>As a result of the improvements to multiple key indexes, the order
of declared columns of the index or constraint has less affect on the
speed of searches than before. If the column that contains more diverse
values appears first, the searches will be slightly faster.</para>
<para>A multi-column index will not speed up queries on the second or
third column only. The first column must be specified in the JOIN .. ON
or WHERE conditions.</para>
<para>Query speed depends a lot on the order of the tables in the JOIN
.. ON or FROM clauses. For example the second query below should be
faster with large tables (provided there is an index on
<literal>TB.COL3</literal>). The reason is that TB.COL3 can be evaluated
very quickly if it applies to the first table (and there is an index on
TB.COL3):</para>
<informalexample>
<programlisting>
(TB is a very large table with only a few rows where TB.COL3 = 4)
SELECT * FROM TA JOIN TB ON TA.COL1 = TB.COL2 AND TB.COL3 = 4;
SELECT * FROM TB JOIN TA ON TA.COL1 = TB.COL2 AND TB.COL3 = 4;</programlisting>
</informalexample>
<para>The general rule is to put first the table that has a narrowing
condition on one of its columns.</para>
<para>1.7.3 features automatic, on-the-fly indexes for views and
subselects that are used in a query. An index is added to a view when it
is joined to a table or another view.</para>
</section>
<section>
<title>Where Condition or Join</title>
<para>Using <literal>WHERE</literal> conditions to join tables is likely
to reduce execution speed. For example the following query will
generally be slow, even with indexes:</para>
<programlisting>
SELECT ... FROM TA, TB, TC WHERE TC.COL3 = TA.COL1 AND TC.COL3=TB.COL2 AND TC.COL4 = 1</programlisting>
<para>The query implies <literal>TA.COL1 = TB.COL2</literal> but does
not explicitly set this condition. If TA and TB each contain 100 rows,
10000 combinations will be joined with TC to apply the column
conditions, even though there may be indexes on the joined columns. With
the JOIN keyword, the <literal>TA.COL1 = TB.COL2</literal> condition has
to be explicit and will narrow down the combination of TA and TB rows
before they are joined with TC, resulting in much faster execution with
larger tables:</para>
<programlisting>
SELECT ... FROM TA JOIN TB ON TA.COL1 = TB.COL2 JOIN TC ON TB.COL2 = TC.COL3 WHERE TC.COL4 = 1</programlisting>
<para>The query can be speeded up a lot more if the order of tables in
joins are changed, so that <literal>TC.COL1 = 1</literal> is applied
first and a smaller set of rows are joined together:</para>
<programlisting>
SELECT ... FROM TC JOIN TB ON TC.COL3 = TB.COL2 JOIN TA ON TC.COL3 = TA.COL1 WHERE TC.COL4 = 1</programlisting>
<para>In the above example the engine automatically applies
<literal>TC.COL4 = 1</literal> to TC and joins only the set of rows that
satisfy this condition with other tables. Indexes on
<literal>TC.COL4</literal>, <literal>TB.COL2</literal> and
<literal>TA.COL1</literal> will be used if present and will speed up the
query.</para>
</section>
<section>
<title>Subqueries and Joins</title>
<para>Using joins and setting up the order of tables for maximum
performance applies to all areas. For example, the second query below
should generally be much faster if there are indexes on TA.COL1 and
TB.COL3:</para>
<example>
<title>Query comparison</title>
<programlisting>
SELECT ... FROM TA WHERE TA.COL1 = (SELECT MAX(TB.COL2) FROM TB WHERE TB.COL3 = 4)
SELECT ... FROM (SELECT MAX(TB.COL2) C1 FROM TB WHERE TB.COL3 = 4) T2 JOIN TA ON TA.COL1 = T2.C1</programlisting>
</example>
<para>The second query turns <literal>MAX(TB.COL2)</literal> into a
single row table then joins it with TA. With an index on
<literal>TA.COL1</literal>, this will be very fast. The first query will
test each row in TA and evaluate <literal>MAX(TB.COL2)</literal> again
and again.</para>
</section>
</section>
<section>
<title>Types and Arithmetic Operations</title>
<para>Table columns of all types supported by HSQLDB can be indexed and
can feature in comparisons. Types can be explicitly converted using the
CONVERT() library function, but in most cases they are converted
automatically. It is recommended not to use indexes on LONGVARBINARY,
LONGVARCHAR and OTHER columns, as these indexes will probably not be
allowed in future versions.</para>
<para>Previous versions of HSQLDB featured poor handling of arithmetic
operations. For example, it was not possible to insert
<literal>10/2.5</literal> into any DOUBLE or DECIMAL column. Since 1.7.0,
full operations are possible with the following rules:</para>
<para>TINYINT, SMALLINT, INTEGER, BIGINT, NUMERIC and DECIMAL (without a
decimal point) are supported integral types and map to byte, short, int,
long and BigDecimal in Java. The SQL type dictates the maximum and minimum
values that can be held in a field of each type. For example the value
range for TINYINT is -128 to +127, although the actual Java type used for
handling TINYINT is <classname>java.lang.Integer</classname>.</para>
<para>REAL, FLOAT, DOUBLE are all mapped to double in Java.</para>
<para>DECIMAL and NUMERIC are mapped to
<classname>java.math.BigDecimal</classname> and can have very large
numbers of digits.</para>
<section>
<title>Integral Types</title>
<para>TINYINT, SMALLINT, INTEGER, BIGINT, NUMERIC and DECIMAL (without a
decimal point) are fully interchangeable internally, and no data
narrowing takes place. Depending on the types of the operands, the
result of the operations is returned in a JDBC
<classname>ResultSet</classname> in any of related Java types:
<classname>Integer</classname>, <classname>Long</classname> or
<classname>BigDecimal</classname>. The
<literal>ResultSet.getXXXX()</literal> methods can be used to retrieve
the values so long as the returned value can be represented by the
resulting type. This type is deterministically based on the query, not
on the actual rows returned. The type does not change when the same
query that returned one row, returns many rows as a result of adding
more data to the tables.</para>
<para>If the SELECT statement refers to a simple column or function,
then the return type is the type corresponding to the column or the
return type of the function. For example:</para>
<informalexample>
<programlisting>
CREATE TABLE t(a INTEGER, b BIGINT); SELECT MAX(a), MAX(b) FROM t;</programlisting>
</informalexample>
<para>would return a result set where the type of the first column is
<filename>java.lang.Integer</filename> and the second column is
<filename>java.lang.Long</filename>. However,</para>
<informalexample>
<programlisting>
SELECT MAX(a) + 1, MAX(b) + 1 FROM t;</programlisting>
</informalexample>
<para>would return <filename>java.lang.Long</filename> and
<classname>BigDecimal</classname> values, generated as a result of
uniform type promotion for all the return values.</para>
<para>There is no built-in limit on the size of intermediate integral
values in expressions. As a result, you should check for the type of the
<classname>ResultSet</classname> column and choose an appropriate
<literal>getXXXX()</literal> method to retrieve it. Alternatively, you
can use the <literal>getObject()</literal> method, then cast the result
to <classname>java.lang.Number </classname> and use the
<literal>intValue()</literal> or <literal>longValue()</literal> methods
on the result.</para>
<para>When the result of an expression is stored in a column of a
database table, it has to fit in the target column, otherwise an error
is returned. For example when <literal>1234567890123456789012 /
12345687901234567890</literal> is evaluated, the result can be stored in
any integral type column, even a TINYINT column, as it is a small
value.</para>
</section>
<section>
<title>Other Numeric Types</title>
<para>In SQL statements, numbers with a decimal point are treated as
DECIMAL unless they are written with an exponent. Thus
<literal>0.2</literal> is considered a DECIMAL value but
<literal>0.2E0</literal> is considered a DOUBLE value.</para>
<para>When <literal>PreparedStatement.setDouble()</literal> or
<literal>setFloat()</literal> is used, the value is treated as a DOUBLE
automatically.</para>
<para>When a REAL, FLOAT or DOUBLE (all synonymous) is part of an
expression, the type of the result is DOUBLE.</para>
<para>Otherwise, when no DOUBLE value exists, if a DECIMAL or NUMERIC
value is part an expression, the type of the result is DECIMAL. The
result can be retrieved from a <classname>ResultSet</classname> in the
required type so long as it can be represented. This means DECIMAL
values can be converted to DOUBLE unless they are beyond the
<literal>Double.MIN_VALUE - Double.MAX_VALUE</literal> range. Similar to
integral values, when the result of an expression is stored in a table
column, it has to fit in the target column, otherwise an error is
returned.</para>
<para>The distinction between DOUBLE and DECIMAL is important when a
division takes place. When the terms are DECIMAL, the result is a value
with a scale (number of digits to the right of the decimal point) equal
to the larger of the scales of the two terms. With a DOUBLE term, the
scale will reflect the actual result of the operation. For example,
<literal>10.0/8.0</literal> (DECIMAL) equals <literal>1.2</literal> but
<literal>10.0E0/8.0E0</literal> (DOUBLE) equals <literal>1.25</literal>.
Without division operations, DECIMAL values represent exact arithmetic;
the resulting scale is the sum of the scales of the two terms when
multiplication is performed.</para>
<para>REAL, FLOAT and DOUBLE values are all stored in the database as
<classname>java.lang.Double</classname> objects. Special values such as
NaN and +-Infinity are also stored and supported. These values can be
submitted to the database via JDBC PreparedStatement methods and are
returned in ResultSet objects.</para>
</section>
<section>
<title>Bit and Boolean Types</title>
<para>Since 1.7.2, BIT is simply an alias for BOOLEAN. The primary
representation of BOOLEAN column is <literal>'true'</literal> or
<literal>'false'</literal> either as the boolean type or as strings when
used from JDBC. This type of column can also be initialised using values
of any numeric type. In this case <literal>0</literal> is translated to
<literal>false</literal> and any other value such as 1 is translated to
<literal>true</literal>.</para>
<para>Since 1.7.3 the BOOLEAN type conforms to the SQL standards and
supports the UNDEFINED state in addition to TRUE or FALSE. NULL values
are treated as undefined. This improvement affects queries that contain
NOT IN. See the test text file, TestSelfNot.txt, for examples of the
queries.</para>
</section>
<section>
<title>Storage and Handling of Java Objects</title>
<para>Since version 1.7.2 this support has improved and any serializable
JAVA Object can be inserted directly into a column of type OTHER using
any variation of <literal>PreparedStatement.setObject()</literal>
methods.</para>
<para>For comparison purposes and in indexes, any two Java Objects are
considered equal unless one of them is NULL. You cannot search for a
specific object or perform a join on a column of type OTHER.</para>
<para>Please note that HSQLDB is not an object-relational database. Java
Objects can simply be stored internally and no operations should be
performed on them other than assignment between columns of type OTHER or
tests for NULL. Tests such as <literal>WHERE object1 =
object2</literal>, or <literal>WHERE object1 = ? </literal>do not mean
what you might expect, as any non-null object would satisfy such a
tests. But <literal>WHERE object1 IS NOT NULL</literal> is perfectly
acceptable.</para>
<para>The engine does not return errors when normal column values are
assigned to Java Object columns (for example assigning an INTEGER or
STRING to such a column with an SQL statement such as <literal>UPDATE
mytable SET objectcol = intcol WHERE ...</literal>) but this is highly
likely to be disallowed in future. So please use columns of type OTHER
only to store your objects and nothing else.</para>
</section>
<section>
<title>Type Size, Precision and Scale</title>
<para>Prior to 1.7.2, all table column type definitions with a column
size, precision or scale qualifier were accepted and ignored.</para>
<para>In 1.8.0, such qualifiers must conform to the SQL standards. For
example INTEGER(8) is no longer acceptable. The qualifiers are still
ignored unless you set a database property. <literal>SET PROPERTY
"sql.enforce_strict_size" TRUE </literal>will enforce sizes for
CHARACTER or VARCHAR columns and pad any strings when inserting or
updating a CHARACTER column. The precision and scale qualifiers are also
enforced for DECIMAL and NUMERIC types. TIMESTAMP can be used with a
precision of 0 or 6 only.</para>
<para>Casting a value to a qualified CHARACTER type will result in
truncation or padding as you would expect. So a test such as
<literal>CAST (mycol AS VARCHAR(2)) = 'xy'</literal> will find the
values beginning with 'xy'. This is the equivalent of
<literal>SUBSTRING(mycol FROM 1 FOR 2)</literal> = 'xy'.</para>
</section>
</section>
<section>
<title>Sequences and Identity</title>
<para>The SEQUENCE keyword was introduced in 1.7.2 with a subset of the
SQL 200n standard syntax. Corresponding SQL 200n syntax for IDENTITY
columns has also been introduced.</para>
<section>
<title>Identity Auto-Increment Columns</title>
<para>Each table can contain one auto-increment column, known as the
IDENTITY column. An IDENTITY column is always treated as the primary key
for the table (as a result, multi-column primary keys are not possible
with an IDENTITY column present). Support has been added for
<literal>CREATE TABLE <tablename>(<colname> IDENTITY,
...)</literal> as a shortcut.</para>
<para>Since 1.7.2, the SQL standard syntax is used by default, which
allows the initial value to be specified. The supported form
is<literal>(<colname> INTEGER GENERATED BY DEFAULT AS
IDENTITY(START WITH n, [INCREMENT BY m])PRIMARY KEY, ...)</literal>.
Support has also been added for <literal>BIGINT</literal> identity
columns. As a result, an IDENTITY column is simply an INTEGER or BIGINT
column with its default value generated by a sequence generator.</para>
<para>When you add a new row to such a table using an <literal>INSERT
INTO <tablename> ...; </literal>statement, you can use the NULL
value for the IDENTITY column, which results in an auto-generated value
for the column. The <literal>IDENTITY() </literal>function returns the
last value inserted into any IDENTITY column by this connection. Use
<literal>CALL IDENTITY(); </literal>as an SQL statement to retrieve this
value. If you want to use the value for a field in a child table, you
can use <literal>INSERT INTO <childtable> VALUES
(...,IDENTITY(),...);</literal>. Both types of call to<literal>
IDENTITY()</literal> must be made before any additional update or insert
statements are issued on the database.</para>
<para>The next IDENTITY value to be used can be set with the
<programlisting>ALTER TABLE ALTER COLUMN <column name> RESTART WITH <new value>;</programlisting></para>
</section>
<section>
<title>Sequences</title>
<para>The SQL 200n syntax and usage is different from what is supported
by many existing database engines. Sequences are created with the
<literal>CREATE SEQUENCE</literal> command and their current value can
be modified at any time with <literal>ALTER SEQUENCE</literal>. The next
value for a sequence is retrieved with the <literal>NEXT VALUE FOR
<name></literal> expression. This expression can be used for
inserting and updating table rows. You can also use it in select
statements. For example, if you want to number the returned rows of a
SELECT in sequential order, you can use:</para>
<example>
<title>Numbering returned rows of a SELECT in sequential order</title>
<programlisting>
SELECT NEXT VALUE FOR mysequence, col1, col2 FROM mytable WHERE ...</programlisting>
</example>
<para>Please note that the semantics of sequences is not exactly the
same as defined by SQL 200n. For example if you use the same sequence
twice in the same row insert query, you will get two different values,
not the same value as required by the standard.</para>
<para>You can query the SYSTEM_SEQUENCES table for the next value that
will be returned from any of the defined sequences. The SEQUENCE_NAME
column contains the name and the NEXT_VALUE column contains the next
value to be returned.</para>
</section>
</section>
<section>
<title>Issues with Transactions</title>
<para>HSQLDB supports transactions at the READ_UNCOMMITTED level, also
known as level 0 transaction isolation. This means that during the
lifetime of a transaction, other connections to the database can see the
changes made to the data. Transaction support works well in general.
Reported bugs concerning transactions being committed if the database is
abruptly closed have been fixed. However, the following issues may be
encountered only with multiple connections to a database using
transactions:</para>
<para>If two transactions modify the same row, no exception is raised when
both transactions are committed. This can be avoided by designing your
database in such a way that application data consistency does not depend
on exclusive modification of data by one transaction. You can set a
database property to cause an exception when this happens.<programlisting>SET PROPERTY "sql.tx_no_multi_rewrite" TRUE</programlisting>When
an <literal>ALTER TABLE .. INSERT COLUMN</literal> or <literal>DROP
COLUMN</literal> command results in changes to the table structure, the
current session is committed. If an uncommitted transaction started by
another connections has changed the data in the affected table, it may not
be possible to roll it back after the <literal>ALTER TABLE</literal>
command. This may also apply to <literal>ADD INDEX</literal> or
<literal>ADD CONSTRAINT</literal> commands. It is recommended to use these
<literal>ALTER</literal> commands only when it is known that other
connections are not using transactions.</para>
<para>After a CHECKPOINT command is issued, uncommitted transactions can
be continued, committed, or rolled back. However, if the database is not
subsequently closed properly with the SHUTDOWN command, any such
transaction that still remains uncommitted at the time of shutdown, is
part committed (to the state at CHECKPOINT) at the next startup. It is
recommended to use the CHECKPOINT command either when no uncommitted
transactions is in progress, or it is known that any such transaction is
not likely to last for such a long time that an abnormal shutdown might
affect its data.</para>
</section>
<section>
<title>New Features and Changes</title>
<para>In recent versions leading to 1.8.0 many enhancements were made for
better SQL support. These are listed in the <link
endterm="sqlsyntax-title" linkend="sqlsyntax-chapter" /> chapter, in
<ulink url="../changelog_1_8_0.txt" /> and <ulink url="../changelog_1_7_2.txt" />.
Functions and expressions such as
POSITION(), SUBSTRING(), NULLIF(), COALESCE(), CASE ... WHEN .. ELSE, ANY,
ALL etc. are among them. Other enhancements may not be very obvious in the
documentation but can result in changes of behaviour from previous
versions. Most significant among these are handling of NULL values in
joins (null columns are no longer joined) and OUTER joins (the results are
now correct). You should test your applications with the new version to
ensure they do not rely on past incorrect behaviour of the engine. The
engine will continue to evolve in future versions towards full SQL
standard support, so it is best not to rely on any non-standard feature of
the current version.</para>
</section>
</chapter>
|