Модераторы: Akella
  

Поиск:

Ответ в темуСоздание новой темы Создание опроса
> Автоматически связать несколько таблиц, INNER JOIN 
:(
    Опции темы
tishaishii
Дата 3.10.2007, 16:08 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Создатель
***


Профиль
Группа: Завсегдатай
Сообщений: 1262
Регистрация: 14.2.2006
Где: Москва

Репутация: нет
Всего: 8



Пишу парсер для макросов для пользователей, чтобы использовать реляционную алгебру. SQL учить категорически отказались. Создал формат макросов и обработчик. Только проблема, когда надо задействовать несколько связанных таблиц.

Хочу сделать, чтобы связанные таблицы указывались через запятую, а способ их связывания брался бы из внешних ключей. Задача: известны имена двух таблиц A и Б, известно, что Б - таблица подчинённая А (есть внешний ключ). Надо создать выражение на SQL, описывающее эту связь (INNER JOIN....).

А дельше, просто приведу примеры макросов, как можно ещё извращаться.

Пример макросов:
Код

%операция1 'ПРОДАЮ'
%операция2 'СДАЮ'
%операция3 'МЕНЯЮ'
%код_базы 2

[LINKS INNER JOIN PAPKA ON LINKS.N_PAPKI=PAPKA.N_PAPKI]

ЕСЛИ 20 КОЛ-ВО
vid_oper это %операция1
data_reg ранее сейчас
data_opl не пусто
id_base это 1

--------------------------

[PAPKA]

ЕСЛИ 30
vid_oper это %операция2
data_reg не ранее сейчас
data_opl не пусто
id_base это %код_базы

ИЗМЕНИТЬ
data_obn это вчера

--------------------------

[PAPKA]

УДАЛИТЬ
n_papki это %операция3


Пример обработчика:
Код

use DBI;
use Data::Dumper;
use locale;

BEGIN {
    %::CMD=(
        'ЕСЛИ'=>'SELECT',
        'ИЗМЕНИТЬ'=>'UPDATE',
        'УДАЛИТЬ'=>'DELETE'
    );
    %::SBST=(
        'СЕГОДНЯ'=>'CURRENT_DATE',
        'TODAY'=>'CURRENT_DATE',
        'NOW'=>'CURRENT_TIMESTAMP',
        'СЕЙЧАС'=>'CURRENT_TIMESTAMP',
        'ВЧЕРА'=>'CURRENT_DATE-1',
        'ЗАВТРА'=>'CURRENT_DATE+1'
    );
    %::CMP=(
        '>'=>'>',
        'БОЛЕЕ'=>'<',
        'ПОЗЖЕ'=>'>',
        'НЕ ПОЗЖЕ'=>'<=',
        'НЕ БОЛЕЕ'=>'<=',
        '<'=>'<',
        'МЕНЕЕ'=>'<',
        'РАНЕЕ'=>'<',
        'НЕ МЕНЕЕ'=>'>=',
        'НЕ РАНЕЕ'=>'>=',
        'ЭТО НЕ'=>'<>',
        '<>'=>'<>',
        '!='=>'<>',
        'НЕ РАВНО'=>'<>',
        'РАВНО'=>'=',
        '='=>'=',
        'ЭТО'=>'=',
        '>='=>'>=',
        'БОЛЕЕ ИЛИ РАВНО'=>'>=',
        '=<'=>'=<',
        'МЕНЕЕ ИЛИ РАВНО'=>'<=',
        'СОДЕРЖИТ'=>'CONTAINING',
        'ТАМ ЕСТЬ'=>'CONTAINING',
        'ТАМ НЕТ'=>'CONTAINING',
        'НЕ СОДЕРЖИТ'=>'NOT CONTAINING',
        'НАЧИНАЕТСЯ С'=>'STARTS WITH',
        'НЕ НАЧИНАЕТСЯ С'=>'NOT STARTS WITH',
        'ПУСТО'=>'IS NULL',
        'НЕ ПУСТО'=>'IS NOT NULL'
    );
}

sub make {
    my($table, $where, $set, $select, $update, $delete, $part, $count)=(
        shift, shift, shift, shift, shift, shift, shift, shift
    );
    $table=$table=~m{\s}?$table:'"'.$table.'"';
    return 'DELETE FROM '.$table.(@$where?' WHERE '.join(' AND ', @$where):undef) if $delete;
    return 'UPDATE '.$table.' SET '.join(', ', @$set).(@$where?' WHERE '.join(' AND ', @$where):undef)
                                                                                    if $update && @$set;
    return 'SELECT '.$part.' '.($count ? $count : '*').' FROM '.$table.
                        (@$where?' WHERE '.join(' AND ', @$where):undef)
}
sub parse {
    my($macro, $table, @where, @set, $mode, $select, $update, $delete, $part, $count, @result, %vars)=shift;
    my$id_firm="ID_FIRM='010'";
    push @where, $id_firm;
    open my$fh, '<'.$macro;binmode $fh;
    while(<$fh>) {
        if(/^\s*\%(\S+)\s+(.*?)\s*$/i) {
            $vars{+uc $1}=$2
        } elsif(/^\s*\[\s*(.+?)\s*\]/) {
            if($table) {
                push @result, &make($table, \@where, \@set, $select, $update, $delete, $part, $count);
                undef $table;
                undef @where;
                push @where, $id_firm;
                undef @set;
                undef $select;
                undef $update;
                undef $part;
                undef $count;
            }
            $table=$1;
        } elsif(/^\s*(\w+)\s*(
                НЕ\s+СОДЕРЖИТ        |
                НЕ\s+РАВНО            |
                НЕ\s+ПУСТО            |
                НЕ\s+БОЛЕЕ            |
                НЕ\s+МЕНЕЕ            |
                НЕ\s+РАНЕЕ            |
                НЕ\s+ПОЗЖЕ            |
                ЭТО\s+НЕ            |
                МЕНЕЕ\s+ИЛИ\s+РАВНО    |
                БОЛЕЕ\s+ИЛИ\s+РАВНО    |
                НЕ\s+НАЧИНАЕТСЯ\s+С    |
                НАЧИНАЕТСЯ\s+С        |
                СОДЕРЖИТ            |
                ТАМ\s+ЕСТЬ            |
                ТАМ\s+НЕТ            |
                РАВНО                |
                ЭТО                    |
                ПУСТО                |
                ПОЗЖЕ                |
                РАНЕЕ                |
                МЕНЕЕ                |
                БОЛЕЕ                |
                \=                    |
                \>                    |
                \=\s*\>                |
                \=\s*\<                |
                \<\s*\>                |
                \<                    |
                \!\s*\=
            )\s*([^\r\n]*)
        /ix) {
            my$cmp=uc $2;
            my($fld, $str)=($1, $3);
            $cmp=~s{\s+}{ }gos;
            $cmp=~s{^\s+|\s+$}{}gos;
            $cmp=$::CMP{$cmp};
            $str=~s{\%(.+?)\b}{
                +exists $vars{+uc $1} ? $vars{+uc $1} : $&
            }geos;
            $str=~s{\b(СЕГОДНЯ|TODAY|NOW|СЕЙЧАС|ВЧЕРА|ЗАВТРА)\b}{
                + exists $::SBST{+uc $1} ? $::SBST{+uc $1} : $1
            }geios;
            push @{$mode?\@set:\@where}, join ' ', $fld, $cmp, $str
        } elsif(/^\s*ЕСЛИ\b\s*(?:(\d+))?\s*(КОЛ(?:ИЧЕСТ|\-)ВО)?\s*$/i) {
            $count=$2?' COUNT(*) ':undef;
            $part=' FIRST '.$1.' ';
            $select=1;
            $mode=undef;
        } elsif(/^\s*УД(?:АЛИТЬ)?\b/) {
            $delete=1;
            $mode=undef;
        } elsif(/^\s*ИЗМ(?:ЕНИТЬ)?\b/i) {
            $update=1;
            $mode=1;
        }
    }
    close $fh;
    +@result, &make($table, \@where, \@set, $select, $update, $delete, $part, $count)
}

#my$dbh=DBI->connect('dbi:ODBC:dsn=DB', 'SYSDBA', 'masterkey');
#my$sth;

foreach(&parse('macro.ini')) {
    print $_, "\n";
#    $sth=$dbh->prepare($_);
#    $sth->finish;
}

#$dbh->disconnect;

PM MAIL ICQ Skype   Вверх
tishaishii
Дата 14.11.2007, 12:22 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Создатель
***


Профиль
Группа: Завсегдатай
Сообщений: 1262
Регистрация: 14.2.2006
Где: Москва

Репутация: нет
Всего: 8



Код

CREATE PROCEDURE JOIN_FIELDS (
    TABLE1 VARCHAR(255),
    TABLE2 VARCHAR(255))
RETURNS (
    FIELD1 VARCHAR(255),
    FIELD2 VARCHAR(255))
AS
begin
  for select
    (SELECT OUTP FROM STR_TRIM(RC1.rdb$relation_name)) || '.' ||
    (SELECT OUTP FROM STR_TRIM(IS1.rdb$field_name)),
    (SELECT OUTP FROM STR_TRIM(RC2.rdb$relation_name)) || '.' ||
    (SELECT OUTP FROM STR_TRIM(IS2.rdb$field_name))
from
    RDB$RELATION_CONSTRAINTS RC1

    INNER JOIN RDB$REF_CONSTRAINTS RFC1 ON
    RC1.rdb$constraint_name=RFC1.rdb$constraint_name

    INNER JOIN RDB$RELATION_CONSTRAINTS RC2 ON
    RC2.rdb$constraint_name=RFC1.rdb$const_name_uq

    INNER JOIN RDB$INDEX_SEGMENTS IS1 ON
    RC1.rdb$index_name=IS1.rdb$index_name

    INNER JOIN RDB$INDEX_SEGMENTS IS2 ON
    RC2.rdb$index_name=IS2.rdb$index_name
where
    RC1.rdb$relation_name=:TABLE1 AND
    RC2.rdb$relation_name=:TABLE2
INTO
    :FIELD1,
    :FIELD2
    do suspend;
end

CREATE PROCEDURE JOIN_TABLES (
    TABLE1 VARCHAR(255),
    TABLE2 VARCHAR(255))
RETURNS (
    RESULT VARCHAR(255))
AS
DECLARE VARIABLE "COUNT" SMALLINT;
DECLARE VARIABLE TMP VARCHAR(255) CHARACTER SET WIN1251;
begin
  SELECT COUNT(*) FROM JOIN_FIELDS(:TABLE1, :TABLE2) INTO :"COUNT";
  if (:"COUNT"=0) then
  begin
    TMP=TABLE1;
    TABLE1=TABLE2;
    TABLE2=TMP;
    SELECT COUNT(*) FROM JOIN_FIELDS(:TABLE1, :TABLE2) INTO :"COUNT";
  end
  if (:"COUNT"=0) then
  BEGIN
    RESULT='';
    SUSPEND;
  END ELSE BEGIN
    RESULT=:TABLE1 || ' INNER JOIN ' || :TABLE2 || ' ON ';
    SUSPEND;
  FOR
    SELECT
        '(' || FIELD1 || '=' || FIELD2 || ')'
    FROM
        JOIN_FIELDS(:TABLE1, :TABLE2)
    INTO
        :RESULT
  DO BEGIN
    if (:"COUNT">1) then
    begin
        RESULT=RESULT || ' AND ';
        "COUNT"=:"COUNT"-1;
    end
    SUSPEND;
  END
  END
end

CREATE PROCEDURE STR_TRIM (
    INP VARCHAR(255))
RETURNS (
    OUTP VARCHAR(255))
AS
DECLARE VARIABLE LEN NUMERIC(3,0);
DECLARE VARIABLE CHR CHAR(1);
begin
  LEN=CHAR_LENGTH(:INP);
  WHILE(:LEN>0) DO
  BEGIN
    CHR=SUBSTRING(:INP FROM :LEN FOR 1);
    if (:CHR<>' ' AND :CHR<>'  ') then
        BREAK;
    LEN=LEN-1;
  END
  OUTP=SUBSTRING(:INP FROM 1 FOR :LEN);
  SUSPEND;
end

PM MAIL ICQ Skype   Вверх
  
Ответ в темуСоздание новой темы Создание опроса
Правила форума "Interbase"
Alex

Обязательно указание:

1. Версию InterBase (Firebird, Yaffil)

2. Способа доступа (ADO, BDE, IBX и т.д.)

  • КАК ПРАВИЛЬНО ОФОРМИТЬ КОД - ЗДЕСЬ
  • КАК ПРАВИЛЬНО УКАЗАТЬ ТЕКСТ ОШИБКИ - ЗДЕСЬ
  • Действия модераторов можно обсудить здесь
  • С просьбами о написании курсовой, реферата и т.п. обращаться сюда
  • FAQ раздела лежит здесь!

Если Вам понравилась атмосфера форума, заходите к нам чаще! С Уважением, Akella.

 
0 Пользователей читают эту тему (0 Гостей и 0 Скрытых Пользователей)
0 Пользователей:
« Предыдущая тема | Firebird, Interbase | Следующая тема »


 




[ Время генерации скрипта: 0.0433 ]   [ Использовано запросов: 22 ]   [ GZIP включён ]


Реклама на сайте     Информационное спонсорство

 
По вопросам размещения рекламы пишите на vladimir(sobaka)vingrad.ru
Отказ от ответственности     Powered by Invision Power Board(R) 1.3 © 2003  IPS, Inc.