Добро пожаловать в форум, Guest  >>   Войти | Регистрация | Поиск | Правила | В избранное | Подписаться
Все форумы / FoxPro, Visual FoxPro Новый топик    Ответить
Топик располагается на нескольких страницах: [1] 2   вперед  Ctrl      все
 Как красиво отобрать первые встретившиеся записи?  [new]
Лунтик
Member

Откуда:
Сообщений: 97
Например, есть таблица
1 Вася
1 Петя
1 Ваня
2 Аня
2 Таня
2 Маня

Нужно получить
1 Вася
2 Аня
24 окт 11, 15:45    [11489900]     Ответить | Цитировать Сообщить модератору
 Re: Как красиво отобрать первые встретившиеся записи?  [new]
tanglir
Member

Откуда:
Сообщений: 28966
Лунтик,

"первые" - это в порядке возрастания recno() или как-то ещё?
А если злобный хаксор Вова ночью откроет эту таблицу и воспользуется командой insert (не insert-sql, а просто insert), и получится у вас
ab
1Вася
1Петя
1Ваня
2Вова
2Аня
2Таня
2Маня
, что тогда делать?
24 окт 11, 15:56    [11489995]     Ответить | Цитировать Сообщить модератору
 Re: Как красиво отобрать первые встретившиеся записи?  [new]
Лунтик
Member

Откуда:
Сообщений: 97
tanglir,

для простоты можно считать, что таблица упорядочивается как нужно непосредственно перед выборкой.

Вообще, пытаюсь решить две задачи, в моем представлении, аналогичные по решению:
1. Выбирать первую операцию за день (период больше дня)
2. Найти первый вариант названия организации в списке строк с одинаковыми ИНН (из разных баз собирается консолидированная таблица контрагентов и получается, что для данного ИНН существует несколько вариантов реквизитов, главное чтобы они были из одной строки, т.е. поля не противоречили друг другу, например, КПП соответствовал бы адресу. Для данной задачи все равно какое значение возьмется, но ,наверно, логично, если возьмется первое)
24 окт 11, 16:09    [11490125]     Ответить | Цитировать Сообщить модератору
 Re: Как красиво отобрать первые встретившиеся записи?  [new]
Koryuu
Member

Откуда: tlt
Сообщений: 97
Первую задачу я бы делал через seek - он находит как раз первое значение
А вот со вторым, по моему опыту, легче как раз брать последнюю запись:
SET ENGINEBEHAVIOR 70
sele * from table1 group by a into cursor outcursor

автор
главное чтобы они были из одной строки, т.е. поля не противоречили друг другу

Семь раз перечитал, чесслово. Не понял :-(
24 окт 11, 16:29    [11490270]     Ответить | Цитировать Сообщить модератору
 Re: Как красиво отобрать первые встретившиеся записи?  [new]
Лунтик
Member

Откуда:
Сообщений: 97
Вот это да! Красивее не бывает - просто живописно!

Почему это вообще работает? Что это за SET ENGINEBEHAVIOR 70? Как Вы на это вышли?

Уважьте, Koryuu, напишите пару слов как этим пользоваться (HELP совсем невнятный).
24 окт 11, 17:02    [11490579]     Ответить | Цитировать Сообщить модератору
 Re: Как красиво отобрать первые встретившиеся записи?  [new]
IgorNG
Member

Откуда: Москва
Сообщений: 956
Лунтик,

Включает режим совместимости SQL-заппросов для версий Visual FoxPro 7.0, 8.0, и 9.0.


SET ENGINEBEHAVIOR 70 | 80 | 90



Параметры
70


Включает режим совместимости команд SQL в системе Visual FoxPro как в версиях, ниже 8.0.
80


Определяет выполнение команд SQL для версии Visual FoxPro 8.0. в следующей таблице описаны особенности данного режима.
Опции SQL SELECT Поведение команды SELECT-SQL
DISTINCT
Вы не можете использовать опцию DISTINCT с полями типа Memo или General. Вместо этого используйте функции PADR( ) или ALLTRIM( ) для этих полей.

Дополнительно см. Функции PADL( ) | PADR( ) | PADC( ) и функция ALLTRIM( ).

UNION
Опция UNION не поддерживается для полей типа Memo без предиката ALL, включенного в команду SQL SELECT.

GROUP BY
В опции GROUP BY должно быть указано поле из списка полей оператора SELECT , для которого выполняется агрегатная функция, такая как COUNT( ). Кроме того, опция GROUP BY должна содержать поле группировки, содержащееся в опции HAVING, исключая поля из агрегатных функций.

Например, следующая команда генерирует ошибку в опции GROUP BY , потому, что поле company не указано в группировке.

Копировать код
SELECT company, country FROM Customer GROUP BY country


Вы можете включать агрегатные функции в список SELECT без указания их в опции GROUP BY.

Например, следующая команда использует COUNT( ) для поля company без указания его в опции GROUP BY .

SELECT COUNT(company), country FROM Customer GROUP BY country

HAVING
Оператор SQL SELECT может содержать опцию HAVING без ссылки на опцию GROUP BY , при этом в операторе SQL SELECT не должно быть агрегатных функций.

Например, следующая команда фильтрует запрос по заданной величине "Sweden".

Копировать код
SELECT customerid FROM customers HAVING country="Sweden"


LIKE
Оператор SQL SELECT автоматически не удаляет "хвостовые" пробелы для значений в конструкции LIKE. В предыдущей версии Visual FoxPro 7.0, в обеих переменных в конструкции LIKE, перед сравнением удалялись "хвостовые" пробелы .

Например, В следующей команде выбираются данные из таблицы, имеющей следующие значения в каждой из строк, соответственно: "1.", "12 ", и "123".

Копировать код
SELECT * FROM table1 WHERE column1 LIKE "1__"


В Visual FoxPro 7.0 и младших версиях в результате вернулась бы одна запись, со значением "123". В Visual FoxPro 8.0 в результат попадают три записи: , "1 ", "12 ", и "123".

Если начальные символы совпадают, а следующие за ним удовлетворяют "маске", и имеют фиксированную длину, то конструкция LIKE обеспечивает их включение в результат запроса, "Хвостовые" пробелы не отбрасываются


90


Определяет выполнение команд SQL в соответствие со стандартными требованиями версии Visual FoxPro 9.0 (По-умолчанию) . В следующей таблице описаны особенности применения оператора SQL SELECT.
Опции SQL SELECT Поведение команды SQL-SELECT
TOP nExpr
Когда используется данная опция: TOPnExpr[PERCENT] , команда SQL SELECT возвращает в результате не больше указанного количества записей: nExpr[PERCENT] . Дополнительно: SQL -средство в этом случае более эффективно использует доступную память и быстрее получает результат запроса, когда опция TOP указывается без ключевого слова PERCENT .

GROUP BY
Когда в запросе используется агрегатная функция, такая как MAX( ), и в команде SQL SELECT отсутствует конструкция GROUP BY , то записей в результате не будет, Visual FoxPro поместит в результат одну запись, системная переменная _TALLY будет = 1. В предыдущих версиях записей в результате не будет, _TALLY = 0.

ORDER BY
Команда SELECT … DISTINCT … ORDER BY генерирует ошибку если в опции ORDER BY указывается поле, которое отсутствует в списке полей команды SELECT .

Копировать код
CREATE CURSOR foo (f1 int, f2 int)SELECT DISTINCT f1 FROM foo ORDER BY f2 INTO CURSOR res


HAVING
Команда SELECT … DISTINCT … HAVING генерирует ошибку если в опции HAVING указано поле, отсутствующее в списке команды SELECT.

Копировать код
CREATE CURSOR foo (f1 int, f2 int)SELECT DISTINCT f1 from foo HAVING f2>1 INTO CURSOR res



Комментарии
Действие команды SET ENGINEBEHAVIOR является глобальным.

Использование установки SET ENGINEBEHAVIOR = 70 может привести к неожиданным результатам при выполнении команды SQL SELECT с опциями DISTINCT и UNION для молей типа MEMO, а также при использовании опций GROUP BY , HAVING без указания конструкции GROUP BY, а также когда используется оператор сравнения LIKE .

Когда используется установка SET ENGINEBEHAVIOR = 70 или = 80, относительно опции TOP в команде SQL SELECT в результат запроса может попасть более чем nExpr записей когда в команде также присутствует опция ORDER BY. Например, Вы задаете nExpr=10. Если в таблице имеется более чем 10 записей с одинаковым значением поля группировки, которое указано в опции ORDER BY , то в результате запроса будет больше чем 10 записей.
24 окт 11, 17:12    [11490669]     Ответить | Цитировать Сообщить модератору
 Re: Как красиво отобрать первые встретившиеся записи?  [new]
reware
Member

Откуда: Хабаровск
Сообщений: 585
Лунтик
Например, есть таблица
1 Вася
1 Петя
1 Ваня
2 Аня
2 Таня
2 Маня

Нужно получить
1 Вася
2 Аня

Класс вопрос !
А у меня (гипотетически) есть таблица
1 Марь Иванна
1 Дядя Федя
2 Тётя из Куйбышева
2 Брат из Магадана
И как мне красиво показать их дни рождения ? :) Чушь полная, пардон.
24 окт 11, 19:00    [11491398]     Ответить | Цитировать Сообщить модератору
 Re: Как красиво отобрать первые встретившиеся записи?  [new]
ВладимирМ
Member

Откуда: г. Москва
Сообщений: 7864
Лунтик
2. Найти первый вариант названия организации в списке строк с одинаковыми ИНН (из разных баз собирается консолидированная таблица контрагентов и получается, что для данного ИНН существует несколько вариантов реквизитов, главное чтобы они были из одной строки, т.е. поля не противоречили друг другу, например, КПП соответствовал бы адресу. Для данной задачи все равно какое значение возьмется, но ,наверно, логично, если возьмется первое)

Я бы крайне НЕ советовал менять настройку SET ENGINEBEHAVIOR. Вы получите большие проблемы при сопровождении программы. Когда понадобиться что-то изменить.

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

Задача поиска одной записи из группы может быть решена "стандартным" Select-SQL только в том случае, если есть некий уникальный идентификатор записи, хотя бы в пределах группы. Физический номер записи на эту роль не годится. Впрочем, этот идентификатор можно и создать. Но все-таки, лучше просто не накапливать "мусор".

Еще раз. Не надо менять настройку SET ENGINEBEHAVIOR. Получите массу проблем в будущем.
24 окт 11, 19:34    [11491592]     Ответить | Цитировать Сообщить модератору
 Re: Как красиво отобрать первые встретившиеся записи?  [new]
SSn888
Member

Откуда:
Сообщений: 340
Лунтик,

Красиво отобрать не получиться, из г... пулю не сделать :)

Если "как вообще отобрать"
1. Выборочка с группировкой по столбцу цифери, с полями "циферь" и "сколько записей с ней"
2. результат "присобачиваем" джойном к имеющемуся курсору, также добавляем поле-нумератор (в "промежуточном итого" имеем поля "номер", "строка", "кол-во строк с таким номером", "нумератор")
3. Нумератору даем значение "порядковое в пределах группы"
Как это все выразить в запросе или нескольких - надеюсь - объяснять не надо

В итоге получим

N Name Cnt nInGr
1 Вася 3 1
1 Петя 3 2
1 Ваня 3 3
2 Аня 4 1
2 Таня 4 2
2 Маня 4 3
2 Дуня 4 4

Из чего делаете выборку примерно по WHERE nInGr == 1
24 окт 11, 19:52    [11491647]     Ответить | Цитировать Сообщить модератору
 Re: Как красиво отобрать первые встретившиеся записи?  [new]
Dima T
Member

Откуда:
Сообщений: 15297
Менять SET ENGINEBEHAVIOR нездоровое решение.
Надежнее примерно так:
select distinct N, space(100) as name from MyTable into cursor result readwrite
index on N tag N
sele MyTable
scan
    if !IndexSeek(MyTable.N, .T., 'result', 'N')
       ? 'Косяк N =',MyTable.N
       loop
   endif
   if empty(result.name)
      * первая запись
      repl in result name with MyTable.Name
   else
      * Повтор, если надо сравниваем текущую запись MyTable с первым найденным (текущая запись result)
   endif
endscan
25 окт 11, 09:03    [11492827]     Ответить | Цитировать Сообщить модератору
 Re: Как красиво отобрать первые встретившиеся записи?  [new]
Лунтик
Member

Откуда:
Сообщений: 97
Пробую LOOKUP(ReturnField, eSearchExpression, SearchedField [, cTagName])

не совсем понятно насчет ReturnField,SearchedField:
ReturnField:Определяет поле, содержание которого LOOKUP( ) возвращает в случае успешного поиска
SearchedField:Определяет поле, в котором осуществлять поиск

Указываю поля через алиас (ТаблицаГдеИскать.ИмяПоля) - вроде работает, указываю в качестве ReturnField RecNo() - вроде работает. Так что там можно указать: поле из текущей области или все-таки выражения да еще и из соседней области.

И по скорости что-то не очень, наверное последний совет с index on поправильнее будет.
А может установить SET ENGINEBEHAVIOR 70 непосредственно перед выборкой, а потом вернуть?
25 окт 11, 11:58    [11494097]     Ответить | Цитировать Сообщить модератору
 Re: Как красиво отобрать первые встретившиеся записи?  [new]
Dima T
Member

Откуда:
Сообщений: 15297
Лунтик
Пробую LOOKUP(ReturnField, eSearchExpression, SearchedField [, cTagName])

И по скорости что-то не очень, наверное последний совет с index on поправильнее будет.
А может установить SET ENGINEBEHAVIOR 70 непосредственно перед выборкой, а потом вернуть?

По скорости: без использования индексов всегда медленно, т.к. каждый раз происходит перебор всех записей до нахождения нужной.

SET ENGINEBEHAVIOR - не гарантирует что попадет именно первая запись, то что обычно первая выбирается не значит что оно так всегда будет работать. Эта настройка появилась из-за того что до 7й версии у фокса был свой язык SQL, а после его привели в соответствии стандартам SQLя, а этот SET оставили чтобы старые проги могли работать без переписывания запросов.

Использовать только SELECTы для твоей задачи нельзя, т.к. в SQLе нет вообще понятия номер записи, как следствие нет первой записи. Все записи равны и их порядок определяется только правилами сортировки.
Поэтому надо использовать SCAN, если скорость критична, то можешь еще так попробовать:
create cursor result (N i, name c(100))
index on N tag N
set order to
sele MyTable
scan
    if !IndexSeek(MyTable.N, .T., 'result', 'N')
      * первая запись
      insert into result (N, name) values (MyTable.N, MyTable.Name)
   else
      * Повтор, если надо сравниваем текущую запись MyTable с первым найденным (текущая запись result)
   endif
endscan
25 окт 11, 12:36    [11494545]     Ответить | Цитировать Сообщить модератору
 Re: Как красиво отобрать первые встретившиеся записи?  [new]
ВладимирМ
Member

Откуда: г. Москва
Сообщений: 7864
Dima T
SET ENGINEBEHAVIOR - не гарантирует что попадет именно первая запись, то что обычно первая выбирается не значит что оно так всегда будет работать.

По условию задачи - не имеет значения первая, последняя или откуда-то из середины. Ему надо всего-лишь, чтобы ВСЕ поля были из одной записи таблицы-источника. Не только те, которые входят в группировку. Скорее всего, ЭТО условие будет соблюдаться. Хотя, согласен, что лучше настройку SET ENGINEBEHAVIOR не трогать

Dima T
Эта настройка появилась из-за того что до 7й версии у фокса был свой язык SQL, а после его привели в соответствии стандартам SQLя

Немного не так расставлены акценты. Свой язык (точнее, диалект) SQL как был в FoxPro, так и остался. Его приблизили к стандарту.

Dima T
Использовать только SELECTы для твоей задачи нельзя, т.к. в SQLе нет вообще понятия номер записи, как следствие нет первой записи. Все записи равны и их порядок определяется только правилами сортировки.

Опять не вполне точно. Да, понятия порядкового номера нет. Но есть понятие "идентификатор записи". Если такой идентификатор существует в таблице, то задача может быть решена чистым Select.

* Предположим, имеем такую структуру данных
* Поле curId - это и есть идентификатор записи
create cursor curTest (curId i, GroupId I, FIO C(10))
insert into curTest values (1, 1, "Вася")
insert into curTest values (2, 1, "Петя")
insert into curTest values (3, 1, "Ваня")
insert into curTest values (4, 2, "Аня")
insert into curTest values (5, 2, "Таня")
insert into curTest values (6, 2, "Маня")

* Берем "первую" запись из группы, определяя "первую" как 
* запись, имеющую минимальное значение поля curId в пределах группы
select * ;
from curTest ;
where not exists(select 1 from curTest tab2 ;
                       where tab2.GroupId = curTest.GroupId ;
                          and tab2.curId < curTest.curId)

В принципе, этот самый идентификатор можно сконструировать искусственно в самом запросе используя для этой цели либо Recno(), либо Integer-Autoincrement (через CAST()). Т.е. сделать подзапросы. Вопрос в том, насколько это будет оправдано. Может, такое поле все-таки есть в таблице?
25 окт 11, 14:12    [11495578]     Ответить | Цитировать Сообщить модератору
 Re: Как красиво отобрать первые встретившиеся записи?  [new]
ВладимирМ
Member

Откуда: г. Москва
Сообщений: 7864
На всякий случай, чтобы не было недоразумений.

В приведенном запросе использовать в качестве идентификатора, например FIO - нельзя, поскольку нет гарантий уникальности этого самого FIO в пределах группы. А использовать все поля при сравнении на больше/меньше в одном запросе - бессмысленно. Неизвестно ведь в какой комбинации будут находится значения из разных записей.

Нужен именно уникальный идентификатор записи. Primary Key. Который бы однозначно идентифицировал записи в таблице.
25 окт 11, 14:23    [11495730]     Ответить | Цитировать Сообщить модератору
 Re: Как красиво отобрать первые встретившиеся записи?  [new]
P000567
Guest
- Привет?
- Привет.
Как звали того грузина котоый утопил в колодце гвозди?

.... 30 часов молчания.

P/S: Заржавелли.

Вот вопрос такой. Ответ будет такой же.
25 окт 11, 15:26    [11496356]     Ответить | Цитировать Сообщить модератору
 Re: Как красиво отобрать первые встретившиеся записи?  [new]
Лунтик
Member

Откуда:
Сообщений: 97
ВладимирМ ,

это то, что надо. У Вас всегда в заначке что-нибудь нужное. СПАСИБО!
25 окт 11, 16:36    [11497126]     Ответить | Цитировать Сообщить модератору
 Re: Как красиво отобрать первые встретившиеся записи?  [new]
sWinTyz
Member

Откуда: Москва
Сообщений: 906
для простоты можно считать, что таблица упорядочивается как нужно непосредственно перед выборкой.

вообще не понятно из-за чего шум
первым запросом в курсор выбираем по ключевым минимальную фамилию
потом левое соединение

иначе говоря операция в два действия
1. создаем курсор отбора
2. отбираем записи по условию
26 окт 11, 13:41    [11502529]     Ответить | Цитировать Сообщить модератору
 Re: Как красиво отобрать первые встретившиеся записи?  [new]
sWinTyz
Member

Откуда: Москва
Сообщений: 906
exists ладно ...
но not exists в фоксе это граната в руках обезьяны
левое соединение быстрее
26 окт 11, 13:43    [11502556]     Ответить | Цитировать Сообщить модератору
 Re: Как красиво отобрать первые встретившиеся записи?  [new]
ВладимирМ
Member

Откуда: г. Москва
Сообщений: 7864
sWinTyz
вообще не понятно из-за чего шум
первым запросом в курсор выбираем по ключевым минимальную фамилию
потом левое соединение

иначе говоря операция в два действия
1. создаем курсор отбора
2. отбираем записи по условию

Теперь представим, что есть две записи с одинаковыми фамилиями. А если есть дубли в других полях? В общем случае, Вам придется повторить описанный цикл для КАЖДОГО поля записи.

Кроме того, а при чем здесь именно левое объединение? Вы вообще в курсе для чего оно предназначено?

sWinTyz
exists ладно ...
но not exists в фоксе это граната в руках обезьяны
левое соединение быстрее

Во-первых, для FoxPro, в общем случае, LEFT JOIN - не быстрее чем NOT EXISTS или NOT IN. Скорость выполнения будет примерно одинакова.

Во-вторых, NOT EXISTS - вполне естесственно "переводится" на "человеческий" язык. Буквально. Здесь нет неоднозначности. "Не существует". Чего здесь непонятного-то?

А вот LEFT JOIN практически все новички интерпретируют не адекватно. Постоянно справшивают, почему результат получается не такой, как вроде бы должен быть, если перевести как "присоединить к левой таблице" (а новички именно ТАК и "переводят"). А подобный перевод именно и вводит в заблуждение. Судя по Вашему предыдущему посту, Вы тоже не вполне понимаете для чего оно используется.

Другими словами, если есть выбор между NOT EXISTS и LEFT JOIN, то используя NOT EXISTS меньше вероятность получить неожиданный (не ожидАемый) результат, чем при использовании LEFT JOIN
26 окт 11, 14:25    [11502973]     Ответить | Цитировать Сообщить модератору
 Re: Как красиво отобрать первые встретившиеся записи?  [new]
sWinTyz
Member

Откуда: Москва
Сообщений: 906
1. фамилии то может быть и две
а группировочное поле одно
да и минимум один

2. насчет NOT EXISTS даже не знаю что сказать ....
я думал это я читал рекомендации Базеяна
черная толстая книга если помните
но видимо память меня сдает
26 окт 11, 15:21    [11503528]     Ответить | Цитировать Сообщить модератору
 Re: Как красиво отобрать первые встретившиеся записи?  [new]
sWinTyz
Member

Откуда: Москва
Сообщений: 906
кстати полученный курсор
насколько я помню можно использовать в опции IN ()
там вроде через запятую можно поля указать ?
ну если нельзя тогда пожно собрать строку
но это будет дольше

так что способов явно больше чем два

а вообще не знаю
я от фокса отшел
человеку который так долго сидит на фоксе конечно виднее
26 окт 11, 15:27    [11503616]     Ответить | Цитировать Сообщить модератору
 Re: Как красиво отобрать первые встретившиеся записи?  [new]
ВладимирМ
Member

Откуда: г. Москва
Сообщений: 7864
sWinTyz
1. фамилии то может быть и две
а группировочное поле одно
да и минимум один

Вы точно понимаете о чем говорите?

create cursor test (f1 I, f2 C(10))
insert into test values (1, 'one')
insert into test values (1, 'one')
insert into test values (2, 'two')

select f1, min(f2) as f2 from test into cursor curGroup nofilter group by f1

* Вот Ваше левое объединение, дающее "парадоксальный" результат
select test.* ;
from test ;
left join curGroup on test.f1 = curGroup.f1 and test.f2 = curGroup.f2

Или как Вы себе представляете второй запрос?
26 окт 11, 15:30    [11503646]     Ответить | Цитировать Сообщить модератору
 Re: Как красиво отобрать первые встретившиеся записи?  [new]
sWinTyz
Member

Откуда: Москва
Сообщений: 906
вообще Вы правы
что там крутили с recno() на фоксе
в старых таблицах нет ИД но рекномер есть
за три запроса можно сделать это
но все это как то не быстро
26 окт 11, 15:35    [11503709]     Ответить | Цитировать Сообщить модератору
 Re: Как красиво отобрать первые встретившиеся записи?  [new]
sWinTyz
Member

Откуда: Москва
Сообщений: 906
эх ... коротка кольчужка ...

create cursor test (f1 I, f2 C(10))
insert into test values (1, 'one')
insert into test values (1, 'one')
insert into test values (2, 'two')

select *,str(recno()) as nn from test into cursor c1 nofilter

select f1, min(f2+nn) as f2 from c1 into cursor curGroup group by f1

select test.* from c1  where nn in (Select substr(nn,11))
26 окт 11, 15:43    [11503814]     Ответить | Цитировать Сообщить модератору
 Re: Как красиво отобрать первые встретившиеся записи?  [new]
sWinTyz
Member

Откуда: Москва
Сообщений: 906
create cursor test (f1 I, f2 C(10))
insert into test values (1, 'one')
insert into test values (1, 'one')
insert into test values (2, 'two')

select *,str(recno()) as nn from test into cursor c1 nofilter

select f1, min(f2+nn) as f2 from c1 into cursor curGroup group by f1

select c1.* from c1  where nn in (Select substr(nn,11) from curGroup)
26 окт 11, 15:44    [11503823]     Ответить | Цитировать Сообщить модератору
Топик располагается на нескольких страницах: [1] 2   вперед  Ctrl      все
Все форумы / FoxPro, Visual FoxPro Ответить