|
0 / 0 / 0
Регистрация: 15.12.2009
Сообщений: 84
|
|
Свойство ConnectionString объекта Connection10.01.2010, 07:52. Показов 8149. Ответов 60
Метки нет (Все метки)
Подскажите пожалуйста:
1.В чем отличие между аргументами Provider и Remote Provider? 2.Аргумент Remote Server задает путь к серверу, тоесть здесь указывается IP адрес? 3.Можно ли использовать Access в качестве сервера, при создании приложений клиент/сервер?
0
|
|
| 10.01.2010, 07:52 | |
|
Ответы с готовыми решениями:
60
Как записать ConnectionString для ADODB.Connection? Свойство ConnectionString выбором провайдера и источника данных Ошибка: Missing Connection or ConnectionString |
|
0 / 0 / 1
Регистрация: 15.12.2007
Сообщений: 381
|
|
| 04.02.2010, 13:49 | |
|
Теперь представь, что такой второй таблицы для заказчика нет.
А телефонов должно быть несколько. Куда ты их запишешь? Можно конечно сделать поле типа TEXT, и закинуть туда два гига телефонов, разделенных запятыми... а если и этого не хватит? Если завтра тебе понадобится сохранять отзывы о заказчике со стороны продавцов товара (а отзывы сами будут по 50 метров с картинками и вавками) что тогда? Теперь не переходя пока к связи многие ко многим - кое какая инфа про техническую сторону проблемы. Когда ты просто создал две таблицы есстесственно связи еще никакой нет. Для ее создания нужно: ALTER TABLE t_phone ADD CONSTRAINT FK_customer_phone FOREIGN KEY (СustomerId) REFERENCES t_customer (ID) ON DELETE CASCADE Итак что здесь написано. ALTER TABLE - покоцать таблицу. ADD CONSTRAINT - добавить ограничение (в нашем случае связь) FOREIGN KEY - вторичный ключ (так называется поле, которое ссылается на первичный ключ) (CustomerId)- название поля в коцаемой таблице, которое мы собираемся заюзать под вторичный ключ. REFERENCES - 'ссылается' t_customer (ID) - таблица и поле в ней на которое ссылаемся. ON DELETE CASCADE - магическое выражение, которое вызывает каскадное удаление. Это когда ты чего-то удаляешь из главной таблицы (в нашем случае таблицы t_customer), а СУБД автоматом ищет в сзязанных таблицах (в нашем случае таблица одна - t_phone) и удаляет все связанные записи (т.е. те записи с телефонами у которых CustomerId ссылается на удаляемого клиента) Еще один технический момент: Если ты сделал связь указанным выше способом. Или без последнего ON DELETE CASCADE, то теперь просто так ты запись в таблицу t_phone не вставишь. А ты ее можешь вставить обязательно привязав к конкретному заказчику. т.е. строка вида INSERT INTO t_phone(Phone, CustomerID) VALUES ('5555555', -1) не прокатит, потому, что заказчика с ID = -1 не существует. Это позволяет сохранить _ссылочную целостность_ Благодяря ей ты точно знаешь, что в базе нет 'ничейных телефонов' и ты знаешь, что если ты пишешь SELECT * FROM t_phone WHERE customer_id = 5 то выберутся все телефоны заказчика под номером пять, ни больше не меньше. .... подолжаем про многие ко многим?
0
|
|
|
0 / 0 / 0
Регистрация: 15.12.2009
Сообщений: 84
|
|
| 05.02.2010, 14:08 [ТС] | |
|
Спасибо за пояснения, если можно то конечно продолжаем, но прежде я хотел кое что уточнить.
Допустим существует две таблицы: t_customer ID Name Address 1 Алексей …………… 2 Петр …………. 3 Василий ……….. t_phone ID Phone CustomerID 1 33333 1 2 44444 1 3 55555 1 4 22222 2 5 66666 2 6 77777 3 1.Поля ID я так понимаю это индексы, они заполняются автоматически при добавлении нового покупателя, то есть я имею ввиду, допустим мы добавляем первого покупателя, тогда в его поле ID запишется 1 автоматически или ее нужно добавлять самому? 2.Создаем связь FK_customer_phone, как ты и писал. Тогда при добавлении нового покупателя поле CustomerID таблицы t_phone будет заполняться автоматически? И как добавить новую запись – так: INSERT INTO t_phone(Phone, CustomerID) VALUES ('5555555', 1)
0
|
|
|
0 / 0 / 1
Регистрация: 15.12.2007
Сообщений: 381
|
|
| 07.02.2010, 11:20 | |
|
1. ID - индексы только в частном случае. В нашем примере ID - прежде всего первичные ключи. Про индексы немного позней.
Если ID (в MSSQL) объявлено как INT IDENTITY(1,1), то это будет автоинкремент и он действительно будет заполняться сам по мере вставки новых значений в таблицу. Если же ID типиризировать как нить по иному, например просто INT, то значения ключей нужно будет подставлять самому. 2. При добавлении новой записи в таблицу t_phone поле CustomerID не заполнится автоматом. Во первых потому, что это не автоинкремент, а во вторых тогда нельзя бы было выполнить INSERT INTO t_phone(Phone, CustomerID) VALUES ('5555555', 1) )А так эта строка вполне валидна, но только в том случае, если customer с ID = 1 существует (при условии, что созданна ссылка) Вопросы?
0
|
|
|
0 / 0 / 0
Регистрация: 15.12.2009
Сообщений: 84
|
|
| 07.02.2010, 13:32 [ТС] | |
|
Да вроде бы пока все понятно. Давай дальше.
0
|
|
|
0 / 0 / 1
Регистрация: 15.12.2007
Сообщений: 381
|
||||||||||||||||||||||||||
| 07.02.2010, 14:02 | ||||||||||||||||||||||||||
|
Итак, многие ко многим.
Логично, что такие связи означают, что записи из первой связанной таблицы соответсвует множество записей из второй таблицы. Одновременно это-же справедливо и для второй таблицы. Т.е. записи из второй таблицы соответствует множество записей из первой. В нашем случае такую связь можно углядеть в связке 'заказчик - товар' Один заказчик может купить несколько наименований товара и каждый товар может быть куплен множеством заказчиков. Техническая реализация сих связей хромает. Поэтому всем миром было принято от таких связей на практике (в технической реалицации) отказаться. Кроме этого существуют т.н. Нормальные формы БД - инфу про которые без проблем найдешь в инете. Связи такого типа разбиваются на две связи один ко многим. Итак пусть было
Данную таблицу - связку можно обозвать напрмер 'Продукты, купленные заказчиком', ну или 'Заказчики, которые купили данный продукт' - технически в ней будут находится связки между заказчиком и продуктом. Например есть заказчик с ID = 1 и продукты с ID = 5, ID = 7, ID = 10 Для того чтобы этому заказчику купить эти продукты пишем:
Вот собственно и все про ссылки. А про индексы... в общем индексы можно обозвать как 'средство ускорения поиска'. При создании ключей индексы генерятся автоматом. Также можно создавать индекс самому. По практически любому полю (исключением являются поля типа text, binary и др.) Создание индекса увеличивает скорость поиска, но уменьшает скорость вставки записи в таблицу. Вощем когда до них дойдешь (а это часто случается когда прога непозволимо долго вытаскивает данные), тогда по этому поводу поговорим.
0
|
||||||||||||||||||||||||||
|
0 / 0 / 0
Регистрация: 15.12.2009
Сообщений: 84
|
|
| 10.02.2010, 09:15 [ТС] | |
|
NickStaves прежде всего хотел бы выразить свою благодарность во многом благодаря тебе я понемногу разобрался с SQL огромное спасибо.
А теперь вопрос: Допустим существует БД по сотрудникам предприятия, в БД отображается куча данных по сотрудникам. Я хотел бы скажем так организовать 'Мастер запросов к БД', то есть допустим сделать как бы конструктор запросов, который содержит кнопки которые соответствую полям в БД. Например кнопки 'Ф.И.О','Дата рождения','Образование' и т.д. Нажимая последовательно на кнопки будет формироваться запрос путем складывания операторов в стрку, которая в дальнейшем будет использоваться для считывания в рекордсет тем самым осущесивляя выборку сотрудников по различным критериям. Это бы позволило пользователю самому организовать запрос к БД. Теперь вопрос насколько сложным или глубоким может быть запрос к SQL, есть ли здесь какие то ограничения?
0
|
|
|
0 / 0 / 1
Регистрация: 15.12.2007
Сообщений: 381
|
|
| 10.02.2010, 11:56 | |
|
Насчет мастера запросов - да. Это легко сделать. Если это действительно нада. Юзерам ведь часто такие разработки кажутся дикими. Им легче нажать на одну кнопку и 'получить всех должников' например.
Ну да ладно. Значит первый совет. Если уж пишешь такую фенечку, то идти нужно не от полям к критериям, а наоборот. Ударение должно ставиться на критериях. Т.е. интерфейс должен выглядеть например так: Сверху набор критериев. Снизу грид - типа результаты. И где-то еще ниже, или сбоку возможность кастомизировать грид. Т.е. исключать кание нить поля или обратно их добавлять. Вот типа самое простое. И работать оно будет только с простыми вещами. Если ты хочешь, чтобы например пользователь вводил книтерии, а запрос делался тупо по ним... (например критерии Имя, Телефон - Два текст бокса, если в первом чего-то есть добавляешь к целевому запросу строку 'AND Name LIKE '' & txtName.Text & '%'', если во втором чего-то - другой SQL, Ну или например название поля выбирается из списка, там дальше парсинг и другая лабуда) ... тогда сильно сложных запросов не сделаешь ... Это будут плоские таблички толку от которых не много. Часто конечному пользователю нужны отчеты - суммы, total, grand total, сравнительные показатели и другая хрень. И очень многое можно сделать при помощи SQL. В реале получается примерно такая ситуация. Пишешь ты БД по сотрудникам, делаешь там ввод, все дела... делаешь вот типа то, что я описал. Т.е. простенькую форму поиска. Далее идешь к заказчику и начинаешь его выспрашивать как ему будет удобно. По каким полям он ищет чаще всего, какие ему нужны отчеты и т.д. И для всей этой новой хрени делаешь отдельные формы или там встраиваешь поисковые контролы в формы редактирования - вощем далее полет фантазии. И второй совет: Никогда не показывай заказчику в проге SQL, а тем более не заставляй его вводить его части (таблицы там, имена полей) - если конечно он сам этого не попросит.
0
|
|
|
0 / 0 / 1
Регистрация: 15.12.2007
Сообщений: 381
|
|
| 10.02.2010, 12:01 | |
|
Теперь на счет сложности и глубокости.
SQL запрос может быть произвольной сложности и глубокости. Максимальный размер его ты врядли когда нить достигнешь, а если достигнешь, то врядли дождешься результатов запроса. Для примера приведу SQL реально работающей проги, заказанной реальными заказчиками. [code] SHAPE {SELECT R.PrinterName AS InitPrintName, P.PrinterName AS RedirectedPrintName, LTRIM(RTRIM(UPPER(R.PrinterName))) + LTRIM(RTRIM(UPPER(P.PrinterName))) AS ID2, LTRIM(STR(SUM(CASE R.cost_type_id WHEN 0 THEN CAST(P.Pages * P.Copies AS FLOAT) * R.costperunit WHEN 1 THEN CAST(P.Pages * P.Copies * R.PageWidth * R.PageHeight AS FLOAT) / 100 / 92903 * R.CostPerUnit END), 12, 2)) AS InitPrintJobCost, LTRIM(STR(SUM(CASE P.cost_type_id WHEN 0 THEN CAST(P.Pages * P.Copies AS FLOAT) * P.costperunit WHEN 1 THEN CAST(P.Pages * P.Copies * P.PageWidth * P.PageHeight AS FLOAT) / 100 / 92903 * P.CostPerUnit END), 12, 2)) AS SavedPrintJobCost, LTRIM(STR(SUM(CASE R.cost_type_id WHEN 0 THEN CAST(P.Pages * P.Copies AS FLOAT) * R.costperunit WHEN 1 THEN CAST(P.Pages * P.Copies * R.PageWidth * R.PageHeight AS FLOAT) / 100 / 92903 * R.CostPerUnit END) - SUM(CASE P.cost_type_id WHEN 0 THEN CAST(P.Pages * P.Copies AS FLOAT) * P.costperunit WHEN 1 THEN CAST(P.Pages * P.Copies * P.PageWidth * P.PageHeight AS FLOAT) / 100 / 92903 * P.CostPerUnit END), 12, 2)) AS SavedValue FROM t_print_jobs P JOIN t_rejected_print_jobs R ON P.r_print_job_id = R.r_print_job_id GROUP BY R.PrinterName, P.PrinterName } APPEND (( SHAPE {SELECT R.reason, LTRIM(RTRIM(UPPER(R.PrinterName))) + LTRIM(RTRIM(UPPER(P.PrinterName))) AS ID2, LTRIM(RTRIM(UPPER(R.PrinterName))) + LTRIM(RTRIM(UPPER(P.PrinterName))) + LTRIM(STR(R.reason)) AS ID1, LTRIM(STR(SUM(CASE R.cost_type_id WHEN 0 THEN CAST(P.Pages * P.Copies AS FLOAT) * R.costperunit WHEN 1 THEN CAST(P.Pages * P.Copies * R.PageWidth * R.PageHeight AS FLOAT) / 100 / 92903 * R.CostPerUnit END), 12, 2)) AS InitPrintJobCost, LTRIM(STR(SUM(CASE P.cost_type_id WHEN 0 THEN CAST(P.Pages * P.Copies AS FLOAT) * P.costperunit WHEN 1 THEN CAST(P.Pages * P.Copies * P.PageWidth * P.PageHeight AS FLOAT) / 100 / 92903 * P.CostPerUnit END), 12, 2)) AS SavedPrintJobCost, LTRIM(STR(SUM(CASE R.cost_type_id WHEN 0 THEN CAST(P.Pages * P.Copies AS FLOAT) * R.costperunit WHEN 1 THEN CAST(P.Pages * P.Copies * R.PageWidth * R.PageHeight AS FLOAT) / 100 / 92903 * R.CostPerUnit END) - SUM(CASE P.cost_type_id WHEN 0 THEN CAST(P.Pages * P.Copies AS FLOAT) * P.costperunit WHEN 1 THEN CAST(P.Pages * P.Copies * P.PageWidth * P.PageHeight AS FLOAT) / 100 / 92903 * P.CostPerUnit END), 12, 2)) AS SavedValue FROM t_print_jobs P JOIN t_rejected_print_jobs R ON P.r_print_job_id = R.r_print_job_id GROUP BY R.PrinterName, P.PrinterName, reason} APPEND ({ SELECT P.Pages * P.Copies AS PagesNum, LTRIM(RTRIM(UPPER(R.PrinterName))) + LTRIM(RTRIM(UPPER(P.PrinterName))) + LTRIM(STR(R.reason)) AS ID1, LTRIM(STR(CASE R.cost_type_id WHEN 0 THEN R.costperunit WHEN 1 THEN CAST(R.PageWidth * R.PageHeight AS FLOAT) / 100 / 92903 * R.CostPerUnit END, 12, 2)) AS InitPageCost, LTRIM(STR(CASE R.cost_type_id WHEN 0 THEN CAST(P.Pages * P.Copies AS FLOAT) * R.costperunit WHEN 1 THEN CAST(P.Pages * P.Copies * R.PageWidth * R.PageHeight AS FLOAT) / 100 / 92903 * R.CostPerUnit END, 12, 2)) AS InitPrintJobCost, LTRIM(STR(CASE P.cost_type_id WHEN 0 THEN P.costperunit WHEN 1 THEN CAST(P.PageWidth * P.PageHeight AS FLOAT) / 100 / 92903 * P.CostPerUnit END, 12, 2)) AS SavedPageCost, LTRIM(STR(CASE P.cost_type_id WHEN 0 THEN CAST(P.Pages * P.Copies AS FLOAT) * P.costperunit WHEN 1 THEN CAST(P.
0
|
|
|
0 / 0 / 0
Регистрация: 15.12.2009
Сообщений: 84
|
|
| 16.02.2010, 11:20 [ТС] | |
|
Привет NickStaves.
В проге которую я переделываю как я уже и говорил многие данные закодированны(например национальность,семейное положение,вид найма,специальность,должность и т.д.)и расшифровка приводится в другой таблице. Можно связать эти две таблицы чтобы получить табл. запросов? Для этого эти коды нужно объявить как индексы и на основании их построить таблицу запросов с которой и считываь данные?
0
|
|
|
0 / 0 / 1
Регистрация: 15.12.2007
Сообщений: 381
|
|
| 16.02.2010, 12:09 | |
|
Ну типа да.
Только я до конца не могу понять что есть таблица запросов. ![]() Значит можно это делать с помощью SQL запросов. А можно с помощью визуальных средств. Загружаешь Enterprise Manager из поставки MSSQL. Добираешься до своей базы. Далее выбираешь Diagrams. New Database Disgram из контекстного меню. Выбираешь твои таблицы (там еще системные будут - их не трогай) Next->Finish А далее выделаешь то, что получилось. Контекстное меню Table View -> Column Names. Далее для создания связи возьми мышью за вторичный ключ из дочерней таблицы и перетащи на главную таблицу (ту, с которой связываешь) Все! Связь создана. Если вторичный ключ нада добавить кликаешь на дочернюю таблицу, контекстное меню Table View->Standart. В получившейся таблице просто добавляешь поле ![]() Мне эта штука PowerDesigner заменяет. Правда меня за это ругают )
0
|
|
|
0 / 0 / 0
Регистрация: 15.12.2009
Сообщений: 84
|
||||||
| 16.02.2010, 14:17 [ТС] | ||||||
|
NickStaves попробовал перекинуть старую БД и столкнился с проблеммой
Не подскажешь что нужно сделать чтобы добавить записи в таблицу?
0
|
||||||
|
0 / 0 / 1
Регистрация: 15.12.2007
Сообщений: 381
|
|
| 16.02.2010, 14:22 | |
|
rs.Open 'SELECT * FROM Table1', cn, adOpenStatic, adLockOptimistic
0
|
|
|
0 / 0 / 0
Регистрация: 15.12.2009
Сообщений: 84
|
||||||
| 17.02.2010, 08:46 [ТС] | ||||||
|
NickStaves такая проблема при переброски данных возникла ошибка в одном поле несоответствие типов и я хотел удалить все записи в новой таблице чтоьы еще раз попробовать. Я выделил все записи и хотел удалить но возникла какая то ошибка короче гворя SQL не дает их удалить таким способом. Неужели для этого нужно тоже писать программу?
Я попробовал, написал процедуру
Quer-based delete failed because the row to delete could not be found Не подскажещь как правильно удалить все записи в таблице? И еще у метода Delet есть параметр AffectRecords в каких случаях его применяют?
0
|
||||||
|
0 / 0 / 1
Регистрация: 15.12.2007
Сообщений: 381
|
|
| 21.02.2010, 11:11 | |
|
Записи из таблицы легче было удалить строкой
cn.execute 'DELETE FROM Table1' А у delete параметр это то, какие записи удалять adAffectCurrent - текущую adAffectGroup - удалить группу, удовлетворяющую текущему состоянию поля Filter. Ну и третий для операций с иерархическими выборками
0
|
|
|
0 / 0 / 0
Регистрация: 15.12.2009
Сообщений: 84
|
|
| 21.02.2010, 13:16 [ТС] | |
|
Привет NickStaves. Хотел поработать дома переписал БД (два файла с расширением *.mdf, *.ldf), но подключить их в SQL не получилось.
Может нужно было как то хитро сохраниться?
0
|
|
|
0 / 0 / 1
Регистрация: 15.12.2007
Сообщений: 381
|
|
| 21.02.2010, 13:43 | |
|
Да .. хитро
![]() Нужно их Deattach на одном месте и Attach на втором. Посмотри в контекстном меню базы AllTasks и т.д. Если не получится я попожжей отвечу. Сейчас немного занят.
0
|
|
|
0 / 0 / 0
Регистрация: 15.12.2009
Сообщений: 84
|
||||||
| 22.02.2010, 06:30 [ТС] | ||||||
|
NickStaves хотелось бы поговорить снова про удаление записей.
Не могу понять почему возникает ошибка типа не найдена строчка(Quer-based delete failed because the row to delete could not be found) при удалении строк, причем ошибка возникает не сразу и при повторении процедуры уже на другой строчке.
0
|
||||||
|
0 / 0 / 1
Регистрация: 15.12.2007
Сообщений: 381
|
|
| 22.02.2010, 11:38 | |
|
А ты попробуй удалять в обратном порядке.
т.е. rs.MoveLast While Not rs.BOF rs.delete rs.moveprevious wend Честно скажу точно не знаю почему такая ситуация получается. Когда чего-то удаляешь из рекордсета, то в начале запись помечается для удаления, а потом, когда ты с нее соскакиваешь или вызываешь Update то происходит реальное удаление из рекордсета (и из базы, если рекордсет серверный). В это время ADO настраивает рекордсет так, чтобы при дальнейших мувах мы перепрыгивали через удаленные строки (их они тоже по моему из памяти удаляют). Какого тут появляется ошибка не знаю.
0
|
|
|
0 / 0 / 0
Регистрация: 15.12.2009
Сообщений: 84
|
|
| 23.02.2010, 07:17 [ТС] | |
|
Подключить БД дома получилось спасибо за совет, а вот удалить записи нет сделал как ты и писал, удалял в обратном порядке, но возникает все таже ошибка.Может здесь все дело в курсоре?
Я попробовал использовать adOpenDynamic и что самое интересное при первом запуске на строчке rs.delet возникла ошибка типа невозможно обработать запрос так как сложное имя таблицы (таблица называлась [Общие сведения]), я переименовал таблицу в Tabl1 и первая запись удалилась а на второй прога зависла долго думала и выдала ошибку [Microsoft][ODBC SQL Server Driver] Timeout expired. Может есть какие соображения по этому поводу?
0
|
|
|
0 / 0 / 1
Регистрация: 15.12.2007
Сообщений: 381
|
|
| 23.02.2010, 10:43 | |
|
А сколько записей в таблице?
А с какими таблицами она связана и как? И сколько записей в связанных таблицах?
0
|
|
| 23.02.2010, 10:43 | |
|
Adodb connection - ощибка в копиях connectionstring
Missing connection or connectionstring - сбиваются настройки подключения к БД not allowed to change the 'connectionString' property. The connection current state is open Свойство ConnectionString не инициализировано Искать еще темы с ответами Или воспользуйтесь поиском по форуму: |
|
Новые блоги и статьи
|
|||
|
GitHub Copilot в JetBrains: модель по умолчанию, Fix и контроль MCP
and_y87 12.10.2026
10 октября 2026 года GitHub обновил Copilot для JetBrains: добавил настройки стартовой модели, исправления из диагностики и управление автозапуском MCP. Поддержка IDE 2025. 1 прекращена — требуется. . .
|
День 5 из 28: Codex подсказывает следующий запрос
and_y87 12.10.2026
9 октября 2026 года OpenAI запустила бету Composer predictions — подсказок следующего сообщения в текущем диалоге Codex. Это помощь с формулировкой запроса, а не автоматическое продолжение задачи.
. . .
|
Запустил конкурс "тем и промптов для текстовых квестов созданных почти чисто ИИ"
Adler 06.10.2026
Всем привет!
За последние три-четыре дня я создал более 16 текстовых квестовых игр используя преимущественно по одному запросу к ИИ на игру. Мне так понравилось смотреть все ветки/ сцены во всех. . .
|
ИИ не может найти нужный язык в списке
Supersumestria 05.10.2026
Я ему даю вот такое изображение и прошу найти и подчеркнуть немецкий язык.
Возвращает он вот это:
https:/ / i. **********/ vqBWLe2. png
Нужную строчку в 3й колонке просто выдумал. .
Это. . .
|
|
Новая последняя моя музыка в SUNO
zorxor 05.10.2026
Здравствуйте, дорогие мои друзья! С большой радостью я хотел бы представить вам свою новую последнею музыку, которую сгенерировала мне по моей просьбе нейросеть SUNO. С уважением, zorxor.
Это. . .
|
Программный домашний кинотеатр
russiannick 27.09.2026
Сподобился на программный домашний кинотеатр. В качестве ЯВУ по традиции выбрал js.
В помощники взял Яндекс-Алису.
Было создано три зала на разные интересы.
исторические и ретро
сериал Хичкок. . .
|
Беседа с ИИ о программистах, недопускающих к созданию и правке кода генеративные ИИ и причины этого
zorxor 21.09.2026
Раньше я радовался или получал некоторые эмоции, пусть небольшие, но всё же, от самого процесса написания кода, рекомпиляции и запуска, видя постепенное развитие программы и прочее. А теперь лень. . .
|
Мобильное приложение ColorStep
pavlinmavlin 17.09.2026
Реализовал приложение Красный, Зеленый, Синий в Unity3d + c#.
Название изменил на ColorStep.
Приложение прошло модерацию и теперь доступно для скачивания. Делал его сам, шаг за шагом — и вот,. . .
|