SQL стандартизирован ISO / IEC. У нас есть замечательные стандарты , такие как SQL-92 , SQL: 1999 , SQL: 2003 , SQL: 2008 , верно? Теоретически. На практике многие вещи не имеют эквивалента между реальными реализациями SQL. Это очень верно для поддержки общих функций, большинство из которых не являются частью какого-либо стандарта. Вот интересное понимание эквивалентных выражений для очень простых функций.
В этой статье я использую jOOQ , библиотеку, которая моделирует SQL как внутренний предметно-ориентированный язык в Java. jOOQ строит модель AST вашего оператора SQL в памяти Java, прежде чем отобразить его на выбранном диалекте SQL. Это помогает стандартизировать SQL, так что ваша модель программирования остается простой, и в то же время имеет доступ к полезным функциям.
Следующая маленькая Java-программа используется:
package org.jooq.compatibility;
import static org.jooq.SQLDialect.SQL99;
import static org.jooq.impl.DSL.*;
import java.sql.Date;
import java.util.EnumSet;
import org.jooq.QueryPart;
import org.jooq.SQLDialect;
import org.jooq.impl.DSL;
public class Compatibility {
// The main method prints a couple of simple
// and common SQL functions
public static void main(String[] args) {
print(sin(3));
print(power(2, 4));
print(sinh(3));
print(lpad(inline("abc"), 3));
print(dateDiff(currentDate(),
Date.valueOf("2000-01-01")));
print(bitCount((byte) 5));
}
private static void print(QueryPart part) {
System.out.println("Printing " + part);
System.out.println("---------------------");
// Get only SQLDialect families, don't
// distinguish between
// SQL Server 2008 or SQL Server 20012
EnumSet<SQLDialect> dialects =
EnumSet.noneOf(SQLDialect.class);
for (SQLDialect dialect:SQLDialect.values())
if (dialect != SQL99)
dialects.add(dialect.family());
// Render the passed in SQL clause to
// all supported SQL dialects
for (SQLDialect dialect: dialects)
System.out.println(
String.format("%1$9s : %2$s",
dialect, DSL.using(dialect)
.renderInlined(part)
));
System.out.println();
System.out.println();
}
}
Теперь проверьте этот вывод!
Печать греха (3)
Это самая простая из протестированных функций. Это делает то же самое на каждом диалекте.
ASE : sin(3)
CUBRID : sin(3)
DB2 : sin(3)
DERBY : sin(3)
FIREBIRD : sin(3)
H2 : sin(3)
HSQLDB : sin(3)
INGRES : sin(3)
MARIADB : sin(3)
MYSQL : sin(3)
ORACLE : sin(3)
POSTGRES : sin(3)
SQLITE : sin(3)
SQLSERVER : sin(3)
SYBASE : sin(3)
Обратите внимание, что SQLite не поддерживает эту функцию. В последующих списках перечислены только те диалекты, которые действительно могут подражать рассматриваемой функции.
В приведенном выше списке ASE — это Sybase ASE, а SYBASE — это Sybase SQL Anywhere.
Мощность печати (2, 4)
Это немного сложнее, так как функция питания поддерживается не во всех базах данных. Но его легко эмулировать, используя логарифмы и выражения.
ASE : power(2, 4)
CUBRID : power(2, 4)
DB2 : power(2, 4)
DERBY : exp((ln(2) * 4))
FIREBIRD : power(2, 4)
H2 : power(2, 4)
HSQLDB : power(2, 4)
INGRES : power(2, 4)
MARIADB : power(2, 4)
MYSQL : power(2, 4)
ORACLE : power(2, 4)
POSTGRES : power(2, 4)
SQLSERVER : power(2, 4)
SYBASE : power(2, 4)
Печать Синх (3)
ХОРОШО. Гиперболический синус точно не используется каждый день. Но когда вам это нужно, вам лучше запомнить формулу. Вы? Вот как эта функция отображает:
ASE : ((exp((3 * 2)) - 1) / (exp(3) * 2))
CUBRID : ((exp((3 * 2)) - 1) / (exp(3) * 2))
DB2 : sinh(3)
DERBY : sinh(3)
FIREBIRD : sinh(3)
H2 : sinh(3)
HSQLDB : ((exp((3 * 2)) - 1) / (exp(3) * 2))
INGRES : ((exp((3 * 2)) - 1) / (exp(3) * 2))
MARIADB : ((exp((3 * 2)) - 1) / (exp(3) * 2))
MYSQL : ((exp((3 * 2)) - 1) / (exp(3) * 2))
ORACLE : sinh(3)
POSTGRES : ((exp((3 * 2)) - 1) / (exp(3) * 2))
SQLITE : sinh(3)
SQLSERVER : ((exp((3 * 2)) - 1) / (exp(3) * 2))
SYBASE : ((exp((3 * 2)) - 1) / (exp(3) * 2))
Печать lpad («abc», 3)
Вне строковых функций, они полезны время от времени. Но даже такие простые функции, как LPAD, поддерживаются не во всех базах данных. Вот пара креативных решений с использованием повтора.
ASE : (replicate(' ', (3 - char_length('abc'))) || 'abc')
CUBRID : lpad('abc', 3, ' ')
DB2 : lpad('abc', 3, ' ')
FIREBIRD : cast(lpad('abc', 3, ' ') as varchar(4000))
H2 : lpad('abc', 3, ' ')
HSQLDB : lpad('abc', 3, ' ')
INGRES : lpad('abc', 3, ' ')
MARIADB : lpad('abc', 3, ' ')
MYSQL : lpad('abc', 3, ' ')
ORACLE : lpad('abc', 3, ' ')
POSTGRES : lpad('abc', 3, ' ')
SQLITE : substr(replace(replace(substr(quote(zeroblob(((3 - length('abc') - 1 + length(' ')) / length(' ') + 1) / 2)), 3), '''', ''), '0', ' '), 1, (3 - length('abc'))) || 'abc'
SQLSERVER : (replicate(' ', (3 - len('abc'))) + 'abc')
SYBASE : (repeat(' ', (3 - length('abc'))) || 'abc')
Ик! Вы видели эту эмуляцию для SQLite? Ужасно. Некоторый фон можно увидеть здесь .
Печать даты ()
Теперь арифметика datetime, вероятно, является наименее совместимой с кросс-вендором областью в SQL. Вы можете проиграть, только если не используете хорошо протестированный инструмент интеграции, такой как jOOQ, для абстрагирования этих функций. Вот что составляет простая разница между двумя датами:
ASE : datediff(day, '2000-01-01', current_date())
CUBRID : current_date() - date '2000-01-01'
DB2 : (days(current_date) - days(date '2000-01-01'))
DERBY : {fn timestampdiff(sql_tsi_day, date('2000-01-01'), current_date) }
FIREBIRD : datediff(day, date '2000-01-01', current_date)
H2 : datediff('day', date '2000-01-01', current_date())
HSQLDB : datediff('day', date '2000-01-01', current_date)
INGRES : cast((current_date - date '2000-01-01') as integer)
MARIADB : datediff(current_date(), date '2000-01-01')
MYSQL : datediff(current_date(), date '2000-01-01')
ORACLE : sysdate - date '2000-01-01'
POSTGRES : current_date - date '2000-01-01'
SQLITE : (strftime('%s', current_date) - strftime('%s', '2000-01-01')) / 86400
SQLSERVER : datediff(day, '2000-01-01', convert(date, current_timestamp))
SYBASE : datediff(day, '2000-01-01', current date)
Хорошая новость заключается в том, что почти в каждой базе данных есть средства для ее реализации. За исключением SQLite, где деление на 86400 секунд на самом деле не экономит летнее время (!).
Печать bit_count (5)
Не в последнюю очередь, давайте посмотрим на этого монстра. Вышеуказанная функция подсчитывает количество битов, установленных в 1 в значении аргумента 5 . Это приведет к 2 . В этом примере рассматриваются только байтовые значения (TINYINT). Вы можете представить, как будет выглядеть поддержка больших целых чисел.
CUBRID : cast(((5 & 1) + ((5 & 2) >> 1) + ((5 & 4) >> 2) + ((5 & 8) >> 3) + ((5 & 16) >> 4) + ((5 & 32) >> 5) + ((5 & 64) >> 6) + ((5 & -128) >> 7)) as int)
DB2 : cast((bitand(5, 1) + (bitand(5, 2) / power(2, 1)) + (bitand(5, 4) / power(2, 2)) + (bitand(5, 8) / power(2, 3)) + (bitand(5, 16) / power(2, 4)) + (bitand(5, 32) / power(2, 5)) + (bitand(5, 64) / power(2, 6)) + (bitand(5, -128) / power(2, 7))) as integer)
FIREBIRD : cast((bin_and(5, 1) + bin_shr(bin_and(5, 2), 1) + bin_shr(bin_and(5, 4), 2) + bin_shr(bin_and(5, 8), 3) + bin_shr(bin_and(5, 16), 4) + bin_shr(bin_and(5, 32), 5) + bin_shr(bin_and(5, 64), 6) + bin_shr(bin_and(5, -128), 7)) as integer)
H2 : cast((bitand(5, 1) + (bitand(5, 2) / power(2, 1)) + (bitand(5, 4) / power(2, 2)) + (bitand(5, 8) / power(2, 3)) + (bitand(5, 16) / power(2, 4)) + (bitand(5, 32) / power(2, 5)) + (bitand(5, 64) / power(2, 6)) + (bitand(5, -128) / power(2, 7))) as int)
HSQLDB : cast((bitand(5, 1) + (bitand(5, 2) / power(2, 1)) + (bitand(5, 4) / power(2, 2)) + (bitand(5, 8) / power(2, 3)) + (bitand(5, 16) / power(2, 4)) + (bitand(5, 32) / power(2, 5)) + (bitand(5, 64) / power(2, 6)) + (bitand(5, -128) / power(2, 7))) as int)
MARIADB : bit_count(5)
MYSQL : bit_count(5)
ORACLE : cast((bitand(5, 1) + (bitand(5, 2) / power(2, 1)) + (bitand(5, 4) / power(2, 2)) + (bitand(5, 8) / power(2, 3)) + (bitand(5, 16) / power(2, 4)) + (bitand(5, 32) / power(2, 5)) + (bitand(5, 64) / power(2, 6)) + (bitand(5, -128) / power(2, 7))) as number(10))
POSTGRES : cast(((5 & 1) + ((5 & 2) >> 1) + ((5 & 4) >> 2) + ((5 & 8) >> 3) + ((5 & 16) >> 4) + ((5 & 32) >> 5) + ((5 & 64) >> 6) + ((5 & -128) >> 7)) as int)
SQLITE : cast(((5 & 1) + ((5 & 2) >> 1) + ((5 & 4) >> 2) + ((5 & 8) >> 3) + ((5 & 16) >> 4) + ((5 & 32) >> 5) + ((5 & 64) >> 6) + ((5 & -128) >> 7)) as int)
SYBASE : cast(((5 & 1) + ((5 & 2) / power(2, 1)) + ((5 & 4) / power(2, 2)) + ((5 & 8) / power(2, 3)) + ((5 & 16) / power(2, 4)) + ((5 & 32) / power(2, 5)) + ((5 & 64) / power(2, 6)) + ((5 & -128) / power(2, 7))) as int)
Самое ужасное в вышесказанном заключается в том, что не только функция bit_count () практически не поддерживается, но и отсутствует побитовая поддержка .
Обратите внимание, что в группе пользователей jOOQ также очень интересно обсудить альтернативные, более короткие способы эмуляции функции bit_count ().
Испуганный?
Не будь В SQL есть больше минных полей, чем совместимости функций между поставщиками. Если вы хотите знать это сами, скачайте jOOQ и запустите собственную эмуляцию функций с помощью вышеуказанной программы.
Следите за будущими сообщениями на эти темы!