Пакеты по группам - PullRequest
       4

Пакеты по группам

0 голосов
/ 24 декабря 2018

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

Пример данных:

CREATE TABLE test01 (id INT PRIMARY KEY CLUSTERED IDENTITY(1, 1) NOT NULL
                   , person_name NVARCHAR(100)
                   , person_surname NVARCHAR(100)
                   , person_group_code CHAR(2) NOT NULL);

INSERT INTO
    dbo.test01 (person_name
              , person_surname
              , person_group_code)
VALUES
  ('n1', 's1', 'g1')
, ('n2', 's2', 'g1')
, ('n3', 's3', 'g1')
, ('n4', 's4', 'g1')
, ('n5', 's5', 'g2')
, ('n6', 's6', 'g2')
, ('n7', 's7', 'g2')
, ('n8', 's8', 'g2')
, ('n9', 's9', 'g2')
, ('n10', 's10', 'g2')
, ('n11', 's11', 'g3')
, ('n12', 's12', 'g3')
, ('n13', 's13', 'g3')
, ('n14', 's14', 'g3');

Моя текущая попытка:

DECLARE @batch_start INT = 1
      , @batch_size INT = 5;
DECLARE @max_id INT = (SELECT MAX(id) FROM dbo.test01);

WHILE @batch_start <= @max_id
    BEGIN
        SELECT *
        FROM dbo.test01
        WHERE id BETWEEN @batch_start AND @batch_start + @batch_size - 1;

        SELECT @batch_start += @batch_size;
    END;

DROP TABLE dbo.test01;

В приведенном выше примере я разделяю 14 строк на 3 пакета: 5строк в пакете № 1, еще 5 строк в пакете № 2 и затем 4 строки в конечном пакете.

Первый пакет (идентификатор от 1 до 5) охватывает только часть группы 'g2', поэтому мне нужнорасширить эту партию, чтобы она покрывала строки 1-10 (мне нужно обработать весь g2 в одной партии).

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

В результате партия № 1 будет охватывать группы g1 и g2 (10 строк), тогда партия № 2 будет охватывать группу g3 (4 строки) и не будетпартия №3 вообще.

Теперь таблица составляет миллиардыколичество строк и пакетов составляет около 50–100 тыс., поэтому мне нужно решение, которое хорошо работает.

Есть ли какие-либо советы о том, как подойти к этому с минимальным ударом по производительности?

Ответы [ 2 ]

0 голосов
/ 24 декабря 2018

Посмотрите, поможет ли ниже:

CREATE TABLE #Temp(g_record_count  int, groupname  varchar(50) )

insert into #Temp(g_record_count,groupname) SELECT MAX(id),person_group_code FROM dbo.test01 group by person_group_code

После этого цикла по этой временной таблице:

DECLARE @rec_per_batch INT = 1
 WHILE @batch_start <= @max_id
BEGIN
    select min(g_record_count) into @rec_per_batch from #temp where  g_record_count>=@batch_size * @batch_start;

    SELECT *
    FROM dbo.test01
    WHERE id BETWEEN @batch_start AND  @rec_per_batch;

    SELECT @batch_start += @batch_size;
END;
0 голосов
/ 24 декабря 2018

Первое, что я заметил, это то, что в вашем текущем коде нет пробелов в столбце идентификаторов - однако это ошибка.Столбец идентификаторов может (и часто имеет) пробелы в числах - поэтому первое, что вы хотите сделать, это использовать row_number() over(order by id), чтобы получить непрерывный порядковый номер для всех ваших записей.

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

I 'Мы использовали переменную таблицы для хранения этих данных для каждого идентификатора в исходной таблице для этой цели, но вы можете использовать временную таблицу и добавить индексы для соответствующих столбцов для повышения производительности.

IВы также переименовали вашу @batch_size переменную в @batch_min_size и добавили несколько других переменных.

Итак, вот переменная таблицы, которую я использовал:

DECLARE @Helper As Table (Id int, Rn int, GroupId int)
INSERT INTO @Helper (Id, Rn, GroupId)
SELECT  Id, 
        ROW_NUMBER() OVER(ORDER BY ID) As Rn,
        ROW_NUMBER() OVER(ORDER BY ID) -
        ROW_NUMBER() OVER(PARTITION BY person_group_code ORDER BY ID) As GroupId        
FROM dbo.test01 

Это содержимоеэтой таблицы:

Id  Rn  GroupId
1   1   0
2   2   0
3   3   0
4   4   0
5   5   4
6   6   4
7   7   4
8   8   4
9   9   4
10  10  4
11  11  10
12  12  10
13  13  10
14  14  10

Я использовал цикл while для выполнения пакетов.В цикле я использовал эту таблицу для вычисления первого и последнего идентификатора каждого пакета, а также номера последней строки пакета.Тогда все, что мне нужно было сделать, это использовать первый и последний идентификатор в предложении where исходной таблицы:

DECLARE @batch_min_size int = 10
      , @batch_end int = 0
      , @batch_start int
      , @first_id_of_batch int
      , @last_id_of_batch int
      , @total_row_count int;

SELECT @total_row_count = COUNT(*) FROM @test01 

WHILE @batch_end < @total_row_count 
BEGIN

    SELECT @batch_start = @batch_end + 1;

    SELECT @batch_end = MAX(Rn)
         , @first_id_of_batch = MIN(Id)
         , @last_id_of_batch = MAX(Id) 
    FROM @Helper 
    WHERE Rn >= @batch_start 
    AND GroupId <= 
    (
        SELECT MAX(GroupId)
        FROM @Helper
        WHERE Rn <= @batch_start + @batch_min_size - 1 
    )


    SELECT id, person_name, person_surname, person_group_code
    FROM dbo.test01 
    WHERE Id >= @first_id_of_batch 
    AND Id <= @last_id_of_batch 

END

Посмотрите живую демонстрацию на rextester.

...