Показать сообщение отдельно
  #5  
Старый 10.07.2008, 21:04
lmikle lmikle вне форума
Модератор
 
Регистрация: 17.04.2008
Сообщения: 8,105
Версия Delphi: 7, XE3, 10.2
Репутация: 49089
По умолчанию

Первое - группировка, не знаю, прокатит или нет, но можно попробовать.

Код:
SELECT 
  RASSTOYANIE,
  PEREVOZCHIK,
  CASE
    WHEN RASSTOYANIE <= 200 THEN 1
    WHEN RASSTOYANIE > 200 AND RASSTOYANIE <= 400 THEN 2
    WHEN RASSTOYANIE > 400 AND RASSTOYANIE <= 600 THEN 3
    ELSE 4
  END AS CATEGOTIYA
FROM VEDOMOST
SORT BY CATEGOTIYA
-- Если здесь не возьмет, то сортировать по полю RASSTOYANIE
-- Это не имеет значения

Это получение. Такой запрос можно и группировать.

Теперь что по поводу установки.
Фактически, тебе надо посчитать кол-во элементов и поделить их в соотв. пропорции (кстати, а у тебя в таблице перевозчиков суммарная доля не больше 100%?).

Тут делаем в несколько шагов.
1. Устанавливаем всем расстояниям <= 200 значение "компания" (делаем запросом, что бы не нагружать сеть - пусть СУБД трудится):
Код:
UPDATE VEDOMOST
SET PEREVOZCHIK = 'компания'
WHERE RASSTOYANIE <= 200

2. Теперь нам надо установить общее кол-во записей для распределения по другим перевозчикам. Опять же делаем всего 1 запрос:
Код:
SELECT count(*) FROM VEDOMOST WHERE RASSTOYANIE > 200

Фактически мы получим кол-во неустановленных строк.

Здесь маленнькая ремарка.
В эти 2 выборки не попадут ведомости, у которых значение расстояния не установлено, т.е. равно NULL. Что бы их добавить в тот или иной запрос, надо в конце добавить "OR RASSTOYANIE IS NULL" (без кавычек, соответсвенно).

3. Теперь нам надо посчитать сколько на 1% доли перевозчиков приходится записей в ведомосях.
Здесь, во первых, стахуемся от того, что суммарная доля м.б. > 100%.
Да, предполагаем, что там у тебя хранятся проценты, т.е. числа от 0 до 100.

Код:
SELECT SUM(DOLYA) FROM PEREVOZCHIKI

Тут мы получили суммарную долю всех перевозчиков в %%.

4. Теперь вычисляем сколько нам надо записей на 1%.
Это уже делаем в Delphi, хотя можно было сделать и за 1 шаг вместе с пп. 2 и 3, но тут есть специфичность от БД, так что не будем торопиться.

Код:
var
  ItemsPerPercent : Integer;
begin
  // Query1 - запрос из шага 2
  // Query2 - запрос из шага 3

  ItemsPerPercent := Round(Query1.Fields[0].AsInteger/Query2.Fields[0].AsInteger);
  If ItemPerPercent = 0 Then Inc(ItemPerPercent);

Очередное замечание. If ItemPerPercent = 0 Then Inc(ItemPerPercent) - страховка от ошибки, когда на 1% приходится меньше 1 записи. В приципе, в таком случае кому-то недостанется чего везти, но это уже их пробема.

5. Теперь нам надо расставить перевозчиков.
Собственно, надо взять каждого перевозчика и посчитать сколько записей приходится на его долю, а затем пройти по ведомостям нужное кол-во строк и поставить их в значение этого перевозчика. Тут нам потребуется 2 запроса и маленький код на Delphi.

Запрос А. Получение перевозчиков. При этом сортируем их обратно пропорционально их доле (наиболее вероятно, что тебе все-таки хочется перевозчиков с большой долей удержать, загрузив их работой).

Код:
SELECT PEREVOZCHIK, DOLYA
FROM PEREVOZCHIKI
ORDER BY DOLYA DESC

Запрос Б. Получение неназначенных ведомостей. Кстати, их тоже можно как-нить отсортировать, но тут я не знаю от чего это может зависей. Запрос аналогичен запросу шага 2, но тут мы получаем список, а не одно число. Здесь нас интересует просто поле, где надо указать перевозчика, т.к. остальны ерасчеты мы уже сделали, ну и поле ID (надеюсь ты его не забыл) для идентификации записи.

Код:
SELECT ID, PEREVOZCHIK FROM VEDOMOST WHERE RASSTOYANIE > 200

И теперь маленький кусочек на Delphi, который расставит перевозчиков.
Сами значения будут устанавливаться с помощью запроса, т.к. мне прсто так удобнее.
Код:
const
  UPD_QUERY = 'UPDATE VEDOMOST SET PEREVOZCHIK = ''%s'' WHERE ID = %d';
var
  N : Integer;
  C : Integer;
begin
  // Query3 - запрос А
  // Query4 - запрос Б
  // Query5 - параметризованный запрос, которым мы и будем ставить значения.
  Query3.Open;
  Query4.Open;
  Query5.SQL.Clear;
  
  Query3.First;
  Query4.First;

  C := 0;
  N := Query3.FieldByName['DOLYA'].AsInteger * ItemsPerPercent; // Кол-во записей для перевозчика.
  While Not Query4.Eof Do
    Begin
      Query5.SQL.Clear;
      Query5.SQL.Add(Format('UPD_QUERY',[ Query3.FieldByName('PEREVOZCHIK'].AsString,Query4.FieldByName['ID'].AsInteger));
      Query5.ExecSQL;
      Inc(C);
      If C > N Then
        Begin
           Query3.Next;
           N := Query3.FieldByName['DOLYA'].AsInteger * ItemsPerPercent; // Кол-во записей для перевозчика.
           C := 0;
        End;
      Query4.Next;
    End;

Собственно, что делается.
N - число записей в ведомостях, которые надо поставить для текущего перевозчика в соответсвии с его долей.
С - текущий счетчик.
Бежим по ведомостям и ставим туда текущего перевозчика. Если для текущего перевозчика мы посавили нужное кол-во ведомостей, то переходим с следующему, пересчитываем N и сбрасываем С.

PS. Можно написать и короче. Для себя бы я сократил всю эту кашу на треть и сделал бы это скорее на сервере (принцип не отличается) в виде хранимой процедуры, а с клиента (из Дельфей) просто дергал бы эту процедуру и выводил результат. Но зато тут подробно рассмотрен алгоритм решения такой задачи.
Ответить с цитированием