выбор части строки

Работа VB и СУБД (Access, MSSQL, MySQL, Oracle и пр.)
Правила форума
При создании новой темы не забывайте указывать используемую СУБД.
psv_nko
Новичок
Новичок
 
Сообщения: 44
Зарегистрирован: 12.04.2005 (Вт) 15:01
Откуда: tula

выбор части строки

Сообщение psv_nko » 03.02.2006 (Пт) 13:25

Требуется решить следующую задачу на Access 2002:
существуют две таблицы: Код (хранятся коды городов различных стран) и Номер (хранятся номера набираемые абонентами); нужно из номера выделить код страны и код города. Проблемой для меня стало то, что количество цифр в коде страны и коде города не постоянно и может меняться от 4 до 7. Подскажите, пожалуйста, в каком направлении двигаться.

Ennor
Конструктивный критик
Конструктивный критик
 
Сообщения: 2504
Зарегистрирован: 18.12.2001 (Вт) 3:58
Откуда: Калуга -> Москва

Сообщение Ennor » 03.02.2006 (Пт) 13:54

Количество знаков в комбинации {город}{номер} всегда постоянно и равно 10. Ну, если брать только цифры, без тире, пробелов, скобок и прочей шелухи. Соответственно, ты всегда можешь сначала вычленить все цифры (думаю, это ты уже сделал), после чего взять крайние 10 символов (функция Right$()), а остаток трактовать как код страны (если он есть, конечно, этот остаток).

Однозначно разделить код города и номер телефона в нем, насколько я помню, невозможно, только если сначала ввести вручную справочную таблицу. Не уверен, что это того стоит.

psv_nko
Новичок
Новичок
 
Сообщения: 44
Зарегистрирован: 12.04.2005 (Вт) 15:01
Откуда: tula

Сообщение psv_nko » 03.02.2006 (Пт) 14:16

тут вот в чем проблема:
например россия:
тула год города 4872, а у алексина 48751
зарубежье
германия 48, а америка 1
Для того что бы пользоваться функциями Left и Right необходимо четко знать сколько символов требуется отрезать. Тут же количесвто символов постоянно меняется в зависимости от того куда звонок проходит.

Ennor
Конструктивный критик
Конструктивный критик
 
Сообщения: 2504
Зарегистрирован: 18.12.2001 (Вт) 3:58
Откуда: Калуга -> Москва

Сообщение Ennor » 03.02.2006 (Пт) 15:00

Хм. Я как-то неадекватно сформулировал, что ли? Ладно, попробую еще раз...

Длина кода города (в цифрах) + длина номера в этом городе (аналогично) = 10. Чем длиннее код, тем короче номер, и наоборот (но код никогда не меньше 3 цифр, наск. я помню).
Можешь принять это за глобальное правило и сделать константу в программе.

Ферштеен зи?

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

psv_nko
Новичок
Новичок
 
Сообщения: 44
Зарегистрирован: 12.04.2005 (Вт) 15:01
Откуда: tula

Сообщение psv_nko » 03.02.2006 (Пт) 16:00

не ферштейн зи. У меня есть таблица с кодами всего мира и соответственно с тарифами по этому направлению. Мне нужно протарифицировать звонок. Для этого надо определить куда он проходил, а сделать это можно только выделив код и сравнив его с кодами направлений в таблице тарификации. Но, особенно часто, в зарубежных странах есть код для стационарных телефонов и код для мобильных, которые отличаются количеством цифр, например, турция стационарный 97, а турция мобильный 972, 9723 и т.д. Короче для того что бы корректно протарифицировать ей нужно выбрать максимально совпадающий код, иначе тариф не будет соответствовать звонку. Эту проблему я решил стандартными методами в аксесе: создавал в запросе несколько полей (5), в которых Left отрезала по 4, 5, 6, 7, 8 цифр, далее посредством Union объединял их и выбирал уже код. На некоторые направления вываливаются несколько кодов в каждом из которых разное количество цифр, и из всех них выбирается максимально совпадающий с кодом тарификации.

Konst_One
Член-корреспондент академии VBStreets
Член-корреспондент академии VBStreets
Аватара пользователя
 
Сообщения: 3041
Зарегистрирован: 09.04.2004 (Пт) 13:47
Откуда: Химки

Сообщение Konst_One » 03.02.2006 (Пт) 16:02

Длина кода города (в цифрах) + длина номера в этом городе (аналогично) = 10


не всегда номер равен 10 цифрам :!:
бывает и 12 и 8 и тд и тп

Ennor
Конструктивный критик
Конструктивный критик
 
Сообщения: 2504
Зарегистрирован: 18.12.2001 (Вт) 3:58
Откуда: Калуга -> Москва

Сообщение Ennor » 03.02.2006 (Пт) 19:06

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

Н-да, ну и задачка. Я, признаться, думал, что давно существует международный стандарт...

psv_nko
Новичок
Новичок
 
Сообщения: 44
Зарегистрирован: 12.04.2005 (Вт) 15:01
Откуда: tula

Сообщение psv_nko » 03.02.2006 (Пт) 19:12

Да сделать то можно все, главное подумать, стандартными средствами (посредством запросов) эта задача решается и работает, но очень громоздко все получается и медленно. Основная проблема в том , что я пока мало ориентируюсь в VB. Мне бы отябы ориентир куда двигаться. А мировой стандарт сущствует, именно в соответствии с ним у нас недавно заменили все первые нули городов на 4.

Ennor
Конструктивный критик
Конструктивный критик
 
Сообщения: 2504
Зарегистрирован: 18.12.2001 (Вт) 3:58
Откуда: Калуга -> Москва

Сообщение Ennor » 03.02.2006 (Пт) 19:41

Окей, попробуем...

Если у тебя номер не длиннее 10 цифр, это значит, что звонок местный (в смысле, внутри РФ). Здесь действует четкий стандарт, согласно которому номер без кода города не бывает больше 7 цифр, а если длина кода + длина номера оказываются меньше 10 цифр (как я понял, они в это же время не могут быть короче 8 ), то в разрыв между ними дописывается эквивалентное количество двоек, чтобы в итоге получились те же 10 цифр.

Теперь если больше. Длина кода страны/сервиса в общем случае переменная, 1-4 цифры (по ходу, всего один локейшн имеет 4-значный код - Guantanamo Bay, 5399, но и это следует учитывать). Ну... берем первые Len() - 10 цифр и ищем среди кодов стран. Если находим, значит наш клиент, дальше все просто. А вот если нет... Получается, нужно брать по очереди 1, 2, 3 и 4 первые цифры номера и искать в таблице кодов стран. В общем случае одним запросом тут не обойдешься никак.

Конечно, я запросто мог чего-то не уловить, но изобилие различных планов телефонной нумерации реально удручает.

skiperski
Идеолог
Идеолог
Аватара пользователя
 
Сообщения: 1386
Зарегистрирован: 25.06.2002 (Вт) 15:52

Сообщение skiperski » 03.02.2006 (Пт) 19:59

Ennor писал(а):Конечно, я запросто мог чего-то не уловить, но изобилие различных планов телефонной нумерации реально удручает.

Это только в случае обработки звонков со всего мира, но задача-то, скорее всего, существенно уже. Я думаю, что нужно обрабобтать только звонки из России, тогда план один, каждый номер уникален, т.е., если в городе А код 123, а в городе Б - 1234 , то, следовательно, в городе А не могут быть номера, начинающиеся с 4. Тогда, если код Германии 48, а Тулы - 4872 и Алексина - 48751, то коды городов в Германии не могут начинаться с 7, что есть абсурд. Скорее всего, все коды городов Германии начинаются к нуля. И звонивший по телефону 48751326549 никак не мог попасть в Германию.

Сначала проверка, как предлагал Ennor десятизначных номеров, потом из таблицы кодов находим самый длинный код и ищем совпадения с ним начальных цифр, совпало - нашли, нет - ищем дальше более короткие аврианты. Но одним запросом, видимо, не обойтись.

psv_nko
Новичок
Новичок
 
Сообщения: 44
Зарегистрирован: 12.04.2005 (Вт) 15:01
Откуда: tula

Сообщение psv_nko » 03.02.2006 (Пт) 20:22

Так, перцы. Хочу кое что дополнить: по международному стандарту количество цифр в номере всегда одинаково, оно сотоит из кода страны + код города + номер, и это не зависимо от того что это за страна, Россия или Парагвай. Структура везде одинаковая. В код направления, опять таки по международному стандарту, входит код страны и код города, т. е. если звониш в Россию в Тулу, то код направления будет не 4872, а 74872, если в Берлин то 48521. Что из себя представляет таблица кодов: это таблица из 3 обязательных полей, где первый это сам код, вторая Страна+Город, третья- тариф. Как я представляю решение этой задачи: нет разницы куда звонили, не стоит делать предварительную проверку на страну, нужно просто выделить максимальносовпадающий код из номера и ВСЕ. Далее все решается еще проще. Что мне нужно: мне нужно что бы Вы, как знающие аксес, направили меня в нужном направлении, а именно как мне выделить этот максимально совпадающий код посредством VB, как без него я и так уже сделал.

skiperski
Идеолог
Идеолог
Аватара пользователя
 
Сообщения: 1386
Зарегистрирован: 25.06.2002 (Вт) 15:52

Сообщение skiperski » 03.02.2006 (Пт) 21:06

Код: Выделить всё
UPDATE Talks, Codes
SET Talks.CodeId = [Codes].[CodeId]
WHERE [Talks].[CodeId]=0 AND Left([Talks].[Number], LenC)=[Codes].[Code]


, где LenC - подстановочное значение длины кода. Сначала ищем самые длинные, потом короче и т.д.

Talks - таблица разговоров
Number - набранный номер
CodeId - код тарифа (изначально равный 0)

Codes - таблица кодов
CodeId - УИН кода
Code - сам код

Получить длины кодов в убывающем порядке можно так:
Код: Выделить всё
SELECT DISTINCT Len(Code) AS LenC
FROM Codes
ORDER BY Len(Code) DESC;

Ennor
Конструктивный критик
Конструктивный критик
 
Сообщения: 2504
Зарегистрирован: 18.12.2001 (Вт) 3:58
Откуда: Калуга -> Москва

Сообщение Ennor » 03.02.2006 (Пт) 22:48

Епрст. Тебе стоило с этого начать, блин.

Я не знаю, поддерживает ли Аксесс тета-джойны, но в сиквеле работают примерно следующие конструкции:
Код: Выделить всё
select top 1 C.Id, CT.DirectionCode, CT.Tariff
from CodeTariffs CT
  inner join Calls C on C.Number like (CT.CompositeCode + '%')
where C.Id = ... -- Id конкретного звонка из таблицы звонков
order by Len(CT.CompositeCode) desc

Правда, у меня символ процента непосредственно в таблице хранится, но то же самое несложно будет сделать и тебе (в Аксессе это кажется астериск - * ). Оператор LIKE в Аксессе точно есть. Вроде все.

psv_nko
Новичок
Новичок
 
Сообщения: 44
Зарегистрирован: 12.04.2005 (Вт) 15:01
Откуда: tula

Сообщение psv_nko » 06.02.2006 (Пн) 15:54

Что то я делаю не так. Вот что у меня есть:
Таблица calls - это таблица разговоров клиентов
Код - порядковый номер записи
cdr_src - идентификационный номер клиента
cdr_dest - номер по которому производился дозвон
cdr_start_time - дата/время звонка
dur - длительность разговора

Таблица codes
направление - соответственно название направления
kod_dest - телефонный код направления
tarif - тариф

Вопрос к skiperski: если не ошибаюсь он у меня будет запрашивать длинны подстановочных кодов? Если да то, это не подходит, он должен сделать все автоматически и отдать мне уже протарифицированный разговор.

skiperski
Идеолог
Идеолог
Аватара пользователя
 
Сообщения: 1386
Зарегистрирован: 25.06.2002 (Вт) 15:52

Сообщение skiperski » 06.02.2006 (Пн) 16:35

У меня встречный вопрос. Что значит отдать? Вернуть в Recordset? Или записать в таблицу calls? Или ещё что-нибудь? И что необходимо подставлять в кач-ве тарифа? сам тариф или его ID?

Если сохранять значения в БД, упрощается процедура обработки: сначала выбираем длины кодов в убывающем порядке, потом в цикле в запрос программно подставляем значения длин. Сохраняем собственно сам тариф или его ID в таблицу calls только в те записи, в которых это поле равно 0 или NULL. Т.о. при обработке заполняются сначала все длинные коды, потом короче, что исключат неоднозначности как в случае с Тулой, Алексиным и Германией. Только нужно проследить чтобы во время отработки этих апдейтов в таблицу calls не добавлялись новые записи. Иначе может получиться, что во время обработки коротких кодов будет добавлен разговор с длинным частично совпадающим с коротким кодом и он будет не правильно тарифицирован.

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

Если в таблице call хранить сам тариф, то удобно быстро расчитать стоимость звонка при изменении самих тарифов, но не понятно как получать инфу о направлении, т.к. ID кода не хранится. Если хранить ID, то получаем простой доступ к любой инфе о тарифе, но при его изменении (ели стоимость разговора не хранится, а вычисляется каждый раз) будут неверно перерасчитаны стоимости разговоров.

Если желаешь получить сразу все данные без предобработки, то сам запрос усложняется (пока не знаю на сколько), т.к. в выборку могут в этом случае попасть дубликаты, как в случае с Алексиным попадут и Тулькие и немецкие звонки, что плохо и надо делать ограничение.

Быть может после такого доп. вопроса и не понабится моё участие в доведении примера относительно твоих конкретных таблиц и полей :)

psv_nko
Новичок
Новичок
 
Сообщения: 44
Зарегистрирован: 12.04.2005 (Вт) 15:01
Откуда: tula

Сообщение psv_nko » 06.02.2006 (Пн) 17:15

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

skiperski
Идеолог
Идеолог
Аватара пользователя
 
Сообщения: 1386
Зарегистрирован: 25.06.2002 (Вт) 15:52

Сообщение skiperski » 06.02.2006 (Пн) 23:29

Код: Выделить всё
SELECT c.*, t.*
FROM ((
(
    SELECT c.[Код] AS CallId, MAX(t.[Кодst]) AS Code
    FROM [calls_2006_01] AS c, [наши тарифы от транстелекома] AS t
    WHERE c.[cdr_dest] LIKE  t.[Кодst] & "*"
    GROUP BY c.[Код]
) AS ct
INNER JOIN [calls_2006_01] AS c ON (c.[Код] = ct.[CallId]))
INNER JOIN [наши тарифы от транстелекома] AS t ON (t.[Кодst] = ct.[Code]))

Отрабатывает довольно долго, т.к. использован оператор LIKE и отрабатывает вся таблица. Ещё раз рекомендую: завести ещё одно поле в таблице calls_2006_01 и сохранять туда значение кода или его ID. Тогда медленный запрос будет отработан только один раз, а выборка результатов будет быстрой.

Кроме того, не все звонки тарифицированы, т.к., видимо, нет соответствующих кодов. Например звонки с кодами 16, 34, 35, 40...

И ещё: таблицы можно было бы и красивей обозвать. И вообще, использование не латинских символов в наименованиях - потенциальная проблема.

psv_nko
Новичок
Новичок
 
Сообщения: 44
Зарегистрирован: 12.04.2005 (Вт) 15:01
Откуда: tula

Сообщение psv_nko » 07.02.2006 (Вт) 20:00

Да, обрабатывает действительно долго. Не протарифицированы потому что был произведен неправильный набор, напротив этого номера всегда стоит 0-я продолжительность разговора. Спасибо за совет на счет дополнительного поля. Вообще спасибо за помощь тебе и Ennor. Я думал, что это нужно делать на VB, а оказывается можно решить все на SQL. Буду рыть в этом направлении, нужно максимально уменьшить время на выборку.


Вернуться в Базы данных

Кто сейчас на конференции

Сейчас этот форум просматривают: нет зарегистрированных пользователей и гости: 1

    TopList  
cron