В. В. Воронин информационное обеспечение систем управления - страница 10

Пример 7. Сделаем выборку минимального, максимального и среднего значений оклада для сотрудников кафедры:

^ SELECT MIN(okl), MAX(okl), AVG(okl) FROM СОТРУДНИКИ

Колонки при этом получают имена MIN_OKL, MAX_OKL, AVG_OKL.

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

Пример 8. Запрос – "Для каждой детали получить суммарное поставленное их количество"

^ SELECT PD.Д_номер, SUM(PD.количество) AS Сумма;

FROM ПОСТАВКА PD;

GROUP BY PD.Д_номер

Этот запрос будет выполняться следующим образом. Сначала строки исходной таблицы будут сгруппированы так, чтобы в каждую группу попали строки с одинаковыми значениями поля Д_номер. Потом внутри каждой группы будет просуммировано поле количество. От каждой группы в результирующую таблицу будет включена одна строка:

Д_Номер

Сумма

1

1250

2

450

3

300

В списке отбираемых полей оператора ^ SELECT, содержащего раздел GROUP BY можно включать только агрегатные функции и поля, которые входят в условие группировки. Следующий запрос выдаст синтаксическую ошибку:

^ SELECT PD.П_номер, PD.Д_номер, SUM(PD.количество) AS Сумма;

FROM ПОСТАВКА PD; GROUP BY PD.Д_номер

Причина ошибки в том, что в список отбираемых полей включено поле ^ П_номер, которое не входит в раздел GROUP BY. И действительно, в каждую полученную группу строк может входить несколько строк с различными значениями поля П_номер. Из каждой группы строк будет сформировано по одной итоговой строке. При этом нет однозначного ответа на вопрос, какое значение выбрать для поля П_номер в итоговой строке. Некоторые диалекты SQL не считают это за ошибку. Запрос будет выполнен, но предсказать, какие значения будут внесены в поле П_номер в результирующей таблице, невозможно.

Пример 9. Запрос – "Получить номера деталей, суммарное поставленное количество которых превосходит 400 единиц".

Условие, заключающееся в том, что суммарное поставленное количество деталей должно быть больше 400 единиц не может быть сформулировано в разделе WHERE, т.к. в этом разделе нельзя использовать агрегатные функции. Условия, использующие агрегатные функции должны быть размещены в специальном разделе HAVING, а именно:

^ SELECT PD.Д_номер, SUM(PD.количество) AS Сумма;

FROM ПОСТАВКА PD; GROUP BY PD.Д_номер;

HAVING SUM(PD.количество) > 400

В результате получим следующую таблицу:

Д_номер

Сумма

1

1250

2

450

В одном запросе могут встретиться как условия отбора строк в разделе ^ WHERE, так и условия отбора групп в разделе HAVING. Условия отбора групп нельзя перенести из раздела HAVING в раздел WHERE. Аналогично и условия отбора строк нельзя перенести из раздела WHERE в раздел HAVING, за исключением условий, включающих поля из списка группировки GROUP BY.

Пример 10. Запрос – "Выдать название всех должностей, количество сотрудников и фонд заработной платы для тех групп должностей, где количество сотрудников больше 1".

^ SELECT B.dol, A.COUNT(*), A.SUM(zps);

FROM СОТРУДНИКИ A, ДОЛЖНОСТИ B;

INTO DBF ФОНД; WHERE A.kdol=B.kdol;

GROOP BY A.kdol HAVING COUNT(*)>1.

Получателем результата выборки может быть DBF-файл, массив, текстовый файл, экран и принтер. Кроме того, информация может быть перенесена в так называемый курсор. Курсор – это временный набор данных, который может быть областью памяти (небольшой объем) или временным файлом на диске (этот процесс от пользователя не зависит) и имеет режим "только чтение". Созданный курсор нельзя изменить и он существует до тех пор, пока его не закроют. Данные курсора могут быть, например, предъявлены командой BROWSE, напечатаны, из них может быть образовано меню и т.д. Курсор может быть обработан другой командой SELECT. К колонкам курсора обращаются по имени, возможно с префиксом – именем курсора (через точку).

Раздел <Получатель> может быть одного из типов.

ARRAY <массив>   задается вновь создаваемый двумерный массив.

CURSOR <курсор>   задается имя курсора.

DBF/TABL   новый файл с заданным именам.

TO FILE <файл> [ADDITIVE]/TO PRINTER –выборка посылается в текстовый файл или на принтер. При задании ADDINIVE выборка добавляется в конец существующего файла без его перезаписи.

Опция ^ PROMPT вызывает окно настройки принтера. Следующие параметры имеют смысл только при выводе на экран: NОCONSOLE – выборка не выдается на экран; PLAIN – заголовки колонок не выдаются; NOWAIT – не делаются паузы при заполнении экрана. Если команда используется без слова INTO, то по умолчанию – вывод на экран.

6.5. Использование встроенных предикатов

Фрагмент <условие фильтра> в разделе WHERE строится из выражений, имеющих смысл только для одного ^ DBF-файла, и допускается использование логических функций. Сложные условия, кроме любых логических функций VFP могут содержать встроенные предикаты SQL. Приведем примеры использования таких встроенных предикатов.

Предикат LIKE – позволяет построить условие сравнения по шаблону, где символ "_" указывает единичный неопределенный символ в строке, а символ ‘%” – любое их количество. Эти символы аналогичны символам маски ‘?’ ‘*’ в MS DOS. Формат предиката <выражение> LIKE <шаблон>.

Пример 11. Пусть в таблице СОТРУДНИКИ есть поле типа memo с именем prim, в котором в частности есть сведения об ученой степени (ктн, дтн, кэн, дэн). Запрос – "Вывести всех сотрудников, имеющих степень ктн".

^ SELECT fio FROM СОТРУДНИКИ WHERE prim LIKE "%ктн%".

Предикат BETWEEN – проверяет, находится ли выражение в указанном диапазоне. Формат предиката
<выражение> BETWEEN AND .

Пример 12. Запрос – "Выбрать фамилии сотрудников, у которых заработная плата находится в диапазоне от 3000 до 5000".

^ SELECT fio FROM СОТРУДНИКИ;

WHERE zps BETWEEN 3000 AND 5000.

Предикат IN проверяет, находится ли выражение, стоящее слева от слова IN, среди перечисленных справа от него. Формат предиката
IN (, , …).

Пример 13."Выдать всех сотрудников, кроме лаборантов и операторов".
^ SELECT fio FROM СОТРУДНИКИ WHERE kdol NOT IN ("8", "9").

Перечисленные предикаты можно комбинировать в сложные выражения с помощью логических функций и скобок.

6.6. Использование подзапросов

Очень удобным средством, позволяющим формулировать запросы более понятным образом, является возможность использования подзапросов, вложенных в основной запрос.

Пример 14. Использование подзапроса в опции WHERE. Запрос – "Получить список поставщиков, статус которых равен максимальному статусу в таблице поставщиков".

^ SELECT * FROM ПОСТАВЩИК P;

WHERE P.statys =;

(SELECT MAX(P.status) FROM P)

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

Результат выполнения запроса будет эквивалентен результату следующей последовательности действий: выполнить один раз вложенный подзапрос и получить максимальное значение статуса; просканировать таблицу поставщиков, каждый раз сравнивая значение статуса поставщика с результатом подзапроса, и отобрать только те строки, в которых статус равен максимальному.

Пример 15. Использование подзапроса в предикате IN. Данный предикат с позиции теории множеств соответствует понятию принадлежности элемента к множеству. Выражение а IN (А) в SQL принимает значение "истина", если аА, и значение "ложь", в противном случае. В простейшем случае множество А задается перечислением своих элементов в скобках через запятую. Запрос – "Получить список поставщиков, поставляющих деталь номер 100"

^ SELECT * FROM ПОСТАВЩИК P;

WHERE P.П_код IN;

(SELECT DISTINCT PD. П_код;

FROM ПОСТАВКА PD;

WHERE PD.Д_код = 100)

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

  1. Выполнить один раз вложенный подзапрос и получить из таблицы ПОСТАВКА список номеров поставщиков, поставляющих деталь номер 100.

  2. Просканировать таблицу ПОСТАВЩИК, каждый раз проверяя, содержится ли номер текущего поставщика в результате подзапроса.

Пример 16. Использование подзапроса в предикате EXIST. Выражение EXIST(А) понимается как "существует хотя бы один элемент множества А" или "множество А не пусто", а NOT EXIST   "множество А пусто" (квантор существования). Запрос – "Получить список поставщиков, поставляющих деталь номер 100"

^ SELECT * FROM ПОСТАВЩИК P;

WHERE EXIST;

(SELECT * FROM ПОСТАВКА PD;

WHERE PD. П_код = P. П_код AND PD. Д_код = 100)

Результат выполнения запроса будет эквивалентен результату следующей последовательности действий.

  1. Просканировать таблицу ПОСТАВЩИК, каждый раз выполняя подзапрос с новым значением номера поставщика, взятым из этой таблицы.

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

В отличие от двух предыдущих примеров, вложенный подзапрос содержит параметр (внешнюю ссылку), передаваемый из основного запроса   номер поставщика П_код. Такие подзапросы называются коррелируемыми. Внешняя ссылка может принимать различные значения для каждой строки-кандидата, оцениваемого с помощью подзапроса, поэтому подзапрос должен выполняться заново для каждой строки, отбираемой в основном запросе. Такие подзапросы характерны для предиката EXIST, но могут быть использованы и в других подзапросах.

Пример 17. Использование подзапроса в предикате NOT EXIST. Запрос – "Получить список поставщиков, не поставляющих деталь номер 100".

^ SELECT * FROM ПОСТАВЩИК P;

WHERE NOT EXIST;

(SELECT * FROM ПОСТАВКА PD;

WHERE PD. П_код = P. П_код AND PD. Д_код = 100)

Также как и в предыдущем примере, здесь используется коррелируемый подзапрос. Отличие в том, что в основном запросе будут отобраны те строки из таблицы поставщиков, для которых вложенный подзапрос не выдаст ни одной строки (пустое множество).

Пример 18. Запрос – "Получить имена всех поставщиков, которые осуществили поставки всех деталей"

^ SELECT DISTINCT П_имя FROM ПОСТАВЩИК P;

WHERE NOT EXIST;

(SELECT * FROM ДЕТАЛИ D;

WHERE NOT EXIST;

(SELECT * FROM ПОСТАВКА PD;

WHERE PD.Д_код = D. Д_код AND

PD. П_код = P. П_код));

Данный запрос содержит два вложенных подзапроса и реализует реляционную операцию деления отношений.

Самый внутренний подзапрос параметризован двумя параметрами
(D. Д_код и P. П_код) и имеет следующий смысл: отобрать в ПОСТАВКА все строки, содержащие данные о поставках поставщика с номером П_код детали с номером Д_код. Отрицание NOT EXIST говорит о том, что данный поставщик не поставляет данную деталь. Внешний к нему подзапрос, сам являющийся вложенным и параметризованным параметром Р.П_код, имеет смысл: отобрать список деталей, которые не поставляются поставщиком П_код. Отрицание NOT EXIST говорит о том, что для поставщика с номером П_код не должно быть деталей, которые не поставлялись бы этим поставщиком. Это в точности означает, что во внешнем запросе отбираются только поставщики, поставляющие все детали.

6.7. Объединение запросов

В SQL имеется еще одна опция в команде выборки данных   UNION, которая позволяет объединить результаты выполнения двух команд SELECT.

Пример 19. Запрос – "Получить имена поставщиков, имеющих статус, больший 3 или поставляющих хотя бы одну деталь номер 100".

^ SELECT P. П_имя FROM ПОСТАВЩИК P;

WHERE P.status > 3;

UNION;

SELECT P. П_имя FROM P, ПОСТАВКА PD;

WHERE P. П_код = PD. П_код AND PD. Д_код = 100

Результирующие таблицы объединяемых запросов должны быть совместимы, т.е. иметь одинаковое количество столбцов и одинаковые типы столбцов в порядке их перечисления. Не требуется, чтобы объединяемые таблицы имели бы одинаковые имена колонок. Это отличает операцию объединения запросов в SQL от операции объединения в реляционной алгебре. Наименования колонок в результирующем запросе будут автоматически взяты из результата первого запроса в объединении. В отличие от не- UNION-запросов, по умолчанию режимом вывода является DISTINCT, а не ALL.

Пример 20. Запрос – "Получить имена и адреса для всех служащих, клиентов и поставщиков". Эти данные будут использованы для автоматической рассылки поздравлений по случаю праздника.

^ SELECT П_имя AS Получатель, адрес AS Mail FROM ПОСТАВЩИК;

UNION;

SELECT fio, e_address FROM АГЕНТ;

UNION;

SELECT fio, address FROM СОТРУДНИКИ

Все команды языка SQL можно продублировать командами ЯМД. Язык SQL становится незаменимым при использовании технологии клиент-сервер. Хотя каждый поставщик серверов баз данных предлагает свою версию SQL и часто один продукт не понимает язык другого, но есть пять базовых команд SQL: INSERT SELECT DELETE UPDATE CREATE.

6.8. Запросы на создание и модификацию таблиц

Рассмотрим команды для реализации простых запросов на добавление, корректировку и удаление записей. Команда

^ DELETE FROM [!]

[WHERE [ AND | OR …]]
реализует запрос на удаление записей из таблицы. Опция FROM определяет исходную таблицу. Опция WHERE помечает на удаление только записи, удовлетворяющие фильтрам, которые объединяются логическими функциями. Например, удалим из списка сотрудников кафедры в июне всех совместителей-студентов (окончание контракта).
^ DELETE FROM BAZA WHERE KODD=10 AND MONTH(DATE())=6.

Команда

UPDATE [!]

SET =

[, = …]

[WHERE [AND | OR …]]
осуществляет запрос на модификацию указанной таблицы. Опция ^ SET перечисляет модифицируемые поля и их новые значения. Опция WHERE определяет критерий отбора записей для модификации. Если предложение WHERE опущено, то корректируются все записи таблицы. Например, поменяем табельный номер всех сотрудников, кроме совместителей и почасовиков, с XXX91 на XXX47 (изменился код кафедры, а почасовики совместители имеют номера в рамках всего ВУЗа)

^ UPDATE BAZA SET TNOM=LEFT(TNOM,3) + “47”

WHERE KODD NOT IN(8, 9).

Команда на добавление одной записи в конец заданного DBF-файла была рассмотрена раньше. Что делать, если необходимо добавить блок данных? Пусть необходимо добавить такие записи в данную таблицу, которые могут быть получены выбором их из другой таблицы базы данных.

С помощью команды ^ INSERT INTO.. . SELECT можно решать эту задачу. Синтаксис данной команды является комбинацией двух ранее рассмотренных команд INSERT и SELECT и имеет следующий вид:

INSERT INTO
[ ]


Вставим во временную таблицу TMP_TABLE нескольких строк, выбранных из другой таблицы ПОСТАВЩИКИ (в таблицу вставляются данные о поставщиках из одного города).

INSERT INTO TMP_TABLE (pkod, pname);
SELECT pnom, name FROM ПОСТАВЩИКИ;
WHERE ПОСТАВЩИКИ.Город="Хабаровск"


Внутри команд удаления и модификации также возможно использование вложенной команды SELECT.

Команда CREATE TABLE создает новую таблицу. Ее синтаксис

CREATE TABLE | DBF [NAME ] [FREE]

( [(n1 [, n2])]

[NULL | NOT NULL]

[CHECK [ERROR ]]

[DEFAULT]

[PRIMARY KEY | UNIQUE]

[REFERENCES [TAG ]]

[NOCPTRANS]

[, FieldName2 ...]

[, PRIMARY KEY TAG

|, UNIQUE TAG ]

[, FOREIGN KEY TAG [NODUP]

REFERENCES [TAG ]]

[, CHECK [ERROR ]])

| FROM ARRAY

Параметр CREATE TABLE | DBF задает имя создаваемой таблицы. Опции TABLE и DBF идентичны. NAME задает длинное имя таблицы. Длинное имя таблицы можно задать только в том случае, когда имеется открытая база данных (DBC-файл), поскольку длинные имена таблиц хранятся в базах данных. Длинное имя может содержать до 128 символов и применяться вместо коротких имен файлов в базах данных.

Опция FREE указывает, что данная таблица не будет включена в открытый DBC-файл. Если таковых нет, параметр FREE можно не задавать.

Фрагмент ( [(n1 [, n2])] задает соответственно имя поля, его тип, ширину и точность (число десятичных знаков). Параметр это буква, обозначающая тип данных поля. Для некоторых типов полей необходимо задавать параметры n1 и/или n2. В табл.6.2. перечислены значения, используемые в качестве , и указывается, необходимо ли при этом задавать n1 и n2.

Опция NULL разрешает иметь в поле значения NULL, а NOT NULL запрещает его. Если опущены параметры NULL и NOT NULL, то допустимость значений NULL в поле определяется текущей установкой SET NULL.


Таблица 6.2.

<тип
поля>

n1

n2

Описание типа данных

C

n

-

Символьное поле ширины n

D

-

-

Дата

T

-

-

Тип DateTime

N

n

d

Числовое поле ширины n, содержащее d десятичных знаков

F

n

d

Плавающее числовое поле ширины n, содержащее d десятичных знаков

I

-

-

Целочисленное

B

-

d

Двойной

Y

-

-

Тип Currency

L

-

-

Логический

M

-

-

Memo

G

-

-

General


Параметр CHECK задает правило контроля для поля. В качестве можно указать пользовательскую функцию. Учтите, что правило проверяется и при присоединении пустой записи. Если данное правило не допускает пустые значения полей в присоединяемой записи, генерируется ошибка. Опция ERROR задает текст сообщения об ошибке, которое VFP выдает при нарушении правила контроля поля. Это сообщение выдается только в том случае, когда происходит изменение данных в окне просмотра или в окне редактирования.

Параметр DEFAULT задает значение, принимаемое в поле по умолчанию. Тип данных выражения должен совпадать с типом данных поля.

Параметр ^ PRIMARY KEY создает для поля первичный индекс. Тег первичного индекса имеет то же имя, что и поле. А параметр UNIQUE создает индекс-кандидат для поля. Имя тега индекса-кандидата совпадает с именем поля. Индексы-кандидаты (создаваемые с помощью опции UNIQUE команды CREATE TABLE или ALTER TABLE - SQL) отличаются от индексов, создаваемых с помощью опции UNIQUE команды INDEX. В индексе, созданном по команде INDEX с опцией UNIQUE, разрешены повторяющиеся ключи индекса; в индексах-кандидатах повторение ключей индекса запрещено. В поле, используемом для первичного индекса или для индекса-кандидата, не допускаются значения NULL и повторяющиеся записи. Однако если вы создали первичный индекс или индекс-кандидат для поля, поддерживающего значения NULL, VFP не будет генерировать ошибку. Ошибка будет сгенерирована в том случае, когда вы попытаетесь ввести в поле, используемое для первичного индекса или индекса-кандидата, значение NULL или повторяющееся значение.

Фрагмент REFERENCES [TAG ] задает родительскую таблицу, с которой устанавливается постоянное динамическая связь. Если опустить аргумент TAG , связь устанавливается на основе первичного индекса родительской таблицы. Если родительская таблица не имеет первичного индекса, VFP генерирует ошибку. Задав TAG , вы установите связь на базе существующего тега структурного индекса родительской таблицы. Имя тега индекса может включать до 10 символов. В качестве родительской таблицы нельзя использовать свободную таблицу.

Опция NOCPTRANS запрещает трансляцию содержимого символьных и memo-полей в другую кодовую страницу. Если таблица преобразуется в другую кодовую страницу, то поля, для которых задана опция NOCPTRANS, транслироваться не будут. NOCPTRANS можно указывать только для символьных и memo-полей. В следующем примере создается таблица BAZA, содержащая два символьных поля и два memo-поля. Для символьного поля Cim2 и memo-поля Mem2 задана опция NOCPTRANS, запрещающая их трансляцию.

CREATE TABLE BAZA (Cim1 C(10), Cim2 C(10) NOCPTRANS,;

Mem1 M, Mem2 M NOCPTRANS)

Параметр ^ PRIMARY KEY TAG задает первичный индекс, который предстоит создать. Выражение задает произвольное поле или комбинацию полей таблицы, а TAG задает имя создаваемого тега первичного индекса. Имя тега индекса может включать до 10 символов. Поскольку таблица может иметь только один первичный индекс, вы не можете задавать это предложение, если уже создали для данного поля первичный индекс. VFP сгенерирует ошибку, если в команду CREATE TABLE включено более одного предложения PRIMARY KEY.

Фраза UNIQUE TAG создает индекс-кандидат. Выражение задает произвольное поле или комбинацию полей таблицы. Однако если вы создали первичный индекс с помощью одной из опций PRIMARY KEY, вы не сможете использовать поле, заданное для первичного индекса, а TAG определяет имя создаваемого тега индекса-кандидата. Имя тега индекса может включать до 10 символов. У таблицы может быть несколько индексов-кандидатов.

Фрагмент ^ FOREIGN KEY TAG [NODUP] создает внешний (отличный от первичного) индекс и устанавливает отношение с родительской таблицей; задает выражение ключа внешнего индекса, а имя создаваемого тега ключа внешнего индекса. Имя тега индекса может включать до 10 символов. Чтобы создать внешний индекс-кандидат, укажите ключевое слово NODUP. Вы можете создать для таблицы несколько внешних индексов, но при этом все выражения внешних индексов должны задавать разные поля в таблице.

Фраза REFERENCES [TAG ] задает родительскую таблицу, с которой устанавливается постоянная связь. Задав TAG , вы установите связь на базе тега индекса родительской таблицы. Имя тега индекса может включать до 10 символов. Если опустить TAG , связь по умолчанию устанавливается с использованием ключа первичного индекса родительской таблицы.

Опция CHECK [ERROR ] задает правило контроля на уровне записи; ERROR задает сообщение об ошибке, которое VFP выдает при выполнении этого правила. Сообщение выдается только в том случае, когда происходит изменение данных в окне редактирования.

Последний параметр ^ FROM ARRAY задает имя существующего массива, содержимое которого представляет собой значение имени, типа, ширины и масштаба для каждого поля таблицы. Подобный массив можно определить с помощью функции AFIELDS( ).

Новая таблица открывается в свободной рабочей области и открывается для монопольного пользования, независимо от установки ^ SET EXCLUSIVE.

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

Если база данных открыта, то для команды CREATE TABLE - SQL требуется монопольное пользование этой базой данных. Чтобы открыть базу данных в монопольном режиме, включите предложение EXCLUSIVE в команду OPEN DATABASE. Если в момент создания новой таблицы нет открытой базы данных, то включение предложения NAME, CHECK, DEFAULT, FOREIGN KEY, PRIMARY KEY или REFERENCES вызовет ошибку.

Заметьте, что в синтаксисе команды CREATE TABLE для разделения определенных опций использованы запятые. Кроме того, предложения NULL, NOT NULL, CHECK, DEFAULT, PRIMARY KEY и UNIQUE должны помещаться внутри скобок, содержащих определения столбцов.

В следующем примере создается новая база данных DATA1. Команда CREATE TABLE используется для создания двух таблиц, TD1 и TD2, которые автоматически включаются в базу данных. Предложения FOREIGN KEY и REFERENCE во второй команде CREATE TABLE создают постоянную связь один-ко-многим между таблицами TD1 и TD2.

CREATE DATABASE DATA1

CREATE TABLE TD1 (tabn c(6) PRIMARY KEY, fio C(20))

CREATE TABLE TD2 (tabn c(6), fio c(20), vr c(2), ;

FOREIGN KEY tabn TAG tabn REFERENCE TD1)

Команда CREATE CURSOR создает временную таблицу. Для каждого поля во временной таблице определяются имя, тип, точность, масштаб, поддержка значений NULL и правила ссылочной целостности. Эти определения можно получить как из самой команды, так и из массива.


4988154617606697.html
4988218442572266.html
4988265712884890.html
4988419012129186.html
4988536103015911.html