суббота, июня 16, 2007

Разработка ПО на php под Oracle 10.X и MySQL

Даты, время

Используйте CURRENT_TIMESTAMP для совместимости с MySQL вместо NOW(), SYSDATE().


Oracle> select CURRENT_TIMESTAMP from dual;
CURRENT_TIMESTAMP
------------------------------------------
14-JUN-07 02.47.52.987970 PM +04:00



mysql> select CURRENT_TIMESTAMP;
+---------------------+
| CURRENT_TIMESTAMP |
+---------------------+
| 2007-06-14 14:58:23 |
+---------------------+
1 row in set (0.00 sec)


Установите стандартное время для MySQL в формате 'YYYY-MM-DD HH24:MI:SS'
Oracle> ALTER SESSION SET NLS_DATE_FORMAT = 'YYYY-MM-DD HH24:MI:SS';
Session altered.

Старайтесь выкручиваться разными приёмами:
mysql> select hour(timediff(now(), t_timestamp)) from table1;
Oracle> select CEIL(SYSDATE - t_timestamp) from table1;


если неудаётся, то ...
Создайте недостающие функции:

FIND_IN_SET
CREATE OR REPLACE FUNCTION
FIND_IN_SET(TWHAT VARCHAR2, TWHERE VARCHAR2) RETURN INTEGER
IS
BEGIN
DECLARE
CDATA varchar2(4000);
CURSOR TDATA IS
SELECT REGEXP_REPLACE(REGEXP_SUBSTR(TWHERE, ','||TWHAT||',|^'||TWHAT||',|^'||TWHAT||'$|,'||TWHAT||'$'),',','') FROM DUAL;
FROM DUAL;
BEGIN
open TDATA;
FETCH TDATA into CDATA;
close TDATA;
IF (TO_CHAR(CDATA) = TWHAT) THEN
return 1;
ELSE
return 0;
END IF;
END;
END FIND_IN_SET;
/


Тесты:

Oracle> SELECT table1.field1 from table1
WHERE find_in_set('5',table1.field1) >= 1;

table1.field1
-------------
',5,1,2,3,4,'

mysql> SELECT table1.field1 from table1
WHERE find_in_set('5',table1.field1) >= 1;

+---------------+
| table1.field1 |
+---------------+
| ,5,1,2,3,4, |
+---------------+
1 row in set (0.00 sec)


CONCAT_WS

Oracle> CREATE OR REPLACE FUNCTION
CONCAT_WS(PATTERN VARCHAR2,
STR1 VARCHAR2 DEFAULT '',
STR2 VARCHAR2 DEFAULT '',
STR3 VARCHAR2 DEFAULT '',
STR4 VARCHAR2 DEFAULT '',
STR5 VARCHAR2 DEFAULT '',
STR6 VARCHAR2 DEFAULT '',
STR7 VARCHAR2 DEFAULT '',
STR8 VARCHAR2 DEFAULT '',
STR9 VARCHAR2 DEFAULT '',
STR10 VARCHAR2 DEFAULT '',
STR11 VARCHAR2 DEFAULT '',
STR12 VARCHAR2 DEFAULT ''
) RETURN VARCHAR2
IS
BEGIN
DECLARE
CDATA varchar2(4000):='';
BEGIN
IF (LENGTH(STR1) > 0) THEN
CDATA := STR1;
END IF;
IF (LENGTH(STR2) > 0) THEN
CDATA := CONCAT(CDATA,PATTERN);
CDATA := CONCAT(CDATA,STR2);
END IF;
IF (LENGTH(STR3) > 0) THEN
CDATA := CONCAT(CDATA,PATTERN);
CDATA := CONCAT(CDATA,STR3);
END IF;
IF (LENGTH(STR4) > 0) THEN
CDATA := CONCAT(CDATA,PATTERN);
CDATA := CONCAT(CDATA,STR4);
END IF;
IF (LENGTH(STR5) > 0) THEN
CDATA := CONCAT(CDATA,PATTERN);
CDATA := CONCAT(CDATA,STR5);
END IF;
IF (LENGTH(STR6) > 0) THEN
CDATA := CONCAT(CDATA,PATTERN);
CDATA := CONCAT(CDATA,STR6);
END IF;
IF (LENGTH(STR7) > 0) THEN
CDATA := CONCAT(CDATA,PATTERN);
CDATA := CONCAT(CDATA,STR7);
END IF;
IF (LENGTH(STR8) > 0) THEN
CDATA := CONCAT(CDATA,PATTERN);
CDATA := CONCAT(CDATA,STR8);
END IF;
IF (LENGTH(STR9) > 0) THEN
CDATA := CONCAT(CDATA,PATTERN);
CDATA := CONCAT(CDATA,STR9);
END IF;
IF (LENGTH(STR10) > 0) THEN
CDATA := CONCAT(CDATA,PATTERN);
CDATA := CONCAT(CDATA,STR10);
END IF;
IF (LENGTH(STR11) > 0) THEN
CDATA := CONCAT(CDATA,PATTERN);
CDATA := CONCAT(CDATA,STR11);
END IF;
IF (LENGTH(STR12) > 0) THEN
CDATA := CONCAT(CDATA,PATTERN);
CDATA := CONCAT(CDATA,STR12);
END IF;
return CDATA;
END;
END;
/

Function created.

Тесты:

Oracle> select concat_ws('::','1','2') from dual;
CONCAT_WS('::','1','2')
------------------------------------------------
1::2

Oracle> select concat_ws('::','1') from dual;
CONCAT_WS('::','1')
------------------------------------------------
1

mysql> select concat_ws('::','1','2');
+-------------------------+
| concat_ws('::','1','2') |
+-------------------------+
| 1::2 |
+-------------------------+
1 row in set (0.00 sec)

mysql> select concat_ws('::','1');
+---------------------+
| concat_ws('::','1') |
+---------------------+
| 1 |
+---------------------+
1 row in set (0.00 sec)


Но не всегда получается сделать универсальный код или создать недостающие функции, слишком проблематично или сложно, учитывая временные рамки. Чтобы выкрутится, переложите часть забот с sql на плечи вашего скриптового языка.

PHP
Если множество решений для разработки приложений поддерживающих множество СУБД. Можно пойти:

1) путем обработки запроса регулярными выражениями подаваемого в php-класс под каждую СУБД(пример в исходных кодах phpbb.com, 3-я версия поддерживает mysql, oracle, postgresql).
Преимущества:

  • можно добавить поддержку СУБД на любом этапе разработки;

Недостатки

  • сложность в составлении регулярных выражениях, есть вероятность ошибки, т.е. в равно приходится возвращаться к старому коду;

2) путем обработки запроса перед тем как подать его в php-класс.
Преимущества:

  • надежность кода, вы всегда знаете как будет выполняться тот или иной запрос;

Недостатки

  • для добавления поддержки новой СУБД придется изменять все запросы во всем коде;

Я выбрал 2 способ:


if($config['driver'] == "mysql"){
$db_limit = " LIMIT 1";
} else {
if($['driver'] == "oracle"){
$db_limit = " AND rownum = 1";
}
}
$sql = "
SELECT field1 f1,field2 f2
FROM table1
WHERE f1 = f2 ".$db_limit;

Если запрос сложнее, то необходимо обрабатывать его начиная с блока WHERE:
(заметьте, что в блоке GROUP BY для Oracle приходится перечислять все поля)


if($config['driver'] == "mysql"){
$db_stuff = "
WHERE f1 = f2
GROUP BY field1
ORDER BY field1 DESC
LIMIT 1";
} else {
if($['driver'] == "oracle"){
$db_stuff = "
WHERE f1 = f2 AND rownum = 1
GROUP BY field1,field2
ORDER BY field1 DESC";
}
}
$sql = "
SELECT field1 f1,field2 f2
FROM table1 ".
$db_stuff;

Еще пример:

if($config['driver'] == "mysql"){
$db_hour = "hour(timediff(now(), t_timestamp))";
} else {
if($config['driver'] == "oracle"){
$db_hour = "CEIL(SYSDATE - t_timestamp)";
}
}
$sql = "
SELECT field1 f1,field2 f2, ".
$db_hour." t_hour"
."FROM table1"


ORA ERRORS

ORA-00923: ключевое слово FROM не найдено там, где оно ожидалось.
возникает, если использовать ключевые слова: number, char, date , ...
в конструкциях:

sql> select somename number, somedate date from sometable;

ORA-00933: неверное завершение SQL-предложения

Возникает, если вы использовали посторонние символы.
Как правило это 'AS', Oracle не понимает эту конструкцию:

Oracle> SELECT table1.field1 AS f FROM table1;

Другие статьи:

  1. Установка и настройка Oracle
  2. Разработка ПО на php под Oracle 10.X и MySQL
  3. Перенос данных с Mysql на Oracle

Комментариев нет: