Как было сказано в комментариях, цикл совершенно не нужен. Способ улучшить производительность любого цикла - полностью удалить его. Циклы являются последним средством в SQL.
Насколько я могу судить, ваша вставка может быть записана одним оператором:
INSERT tbl_bill(clid, hlid, holdingNo,ClientID, ClientName, billno, date_month, unit, others, fine, due, bill, rebate, remark, payment_date, inserted_by, inserted_date)
SELECT clid = c.id,
hlid = h.id,
h.holdinNo ,
c.cliendID,
clientName = CAST(c.clientName AS NVARCHAR(50)),
BillNo = CONCAT(h.holdinNo, MONTH(@tdate), YEAR(@tdate)),
date_month = @tDate,
unit = 0,
others = CASE WHEN h.hfloor = 0 THEN rs.frate * (h.hfloor - 1) ELSE 0 END,
fine = bs.FineRate * b.Due / 100,
due = b.Due,
bill = @bill, -- This is declared but never assigned
rebate = bs.rebate,
remark = @remark,
payment_date = @pdate,
inserted_by = @userid,
inserted_date = GETDATE()
FROM ( SELECT id, clientdID, ClientName
FROM tbl_client
WHERE status = 1
) AS c
INNER JOIN
( SELECT id, holdinNo, [floor], connect_radius
FROM tx_holding
WHERE status = 1
AND connect_radius <> '0'
AND type = 'Residential'
) AS h
ON c.id = h.clid
LEFT JOIN tbl_radius_setting AS rs
ON rs.radius= CONVERT(real,h.connect_radius)
AND rs.status = 1
AND rs.type = 'Non-Govt.'
LEFT JOIN tbl_bill_setting AS bs
ON bs.Status = 1
LEFT JOIN
( SELECT hlid,
SUM(netbill) AS Due
FROM tbl_bill AS b
WHERE date_month < @tdate
AND (b.ispay = 0 OR b.ispay IS NULL)
GROUP BY hlid
) AS b
ON b.hlid = h.id
WHERE NOT EXISTS
( SELECT 1
FROM tbl_bill AS tb
WHERE EOMONTH(@tdate) = EOMONTH(date_month)
AND tb.holdingNo = h.holdinNo
AND (tb.update_by IS NOT NULL OR tb.ispay=1)
);
Пожалуйста, примите это с щепоткой солиЭто была довольно тяжелая работа, пытаясь собрать воедино логику, поэтому, возможно, потребуются некоторые незначительные изменения и исправления
Помимо адаптации этого к работе в качестве единого утверждения, я сделал ряд модификаций. к существующему коду:
- Поменяйте местами
NOT IN
на NOT EXISTS
, чтобы избежать проблем с нулевыми записями. Если holdingNo
обнуляем, они эквивалентны, если holdingNo
обнуляем, NOT EXISTS
безопаснее - Не существует против НЕ IN - Используемый вами синтаксис объединения был заменен 27лет назад, поэтому я перешел с синтаксиса соединения ANSI-89 на ANSI-92. - Вредные привычки к удару: использование ДЖОЙНОВ в старом стиле
- Изменены предикаты
YEAR(date_month) = YEAR(@tDate) AND MONTH(date_month) = MONTH(@tDate)
, чтобы они стали EOMONTH(@tdate) = EOMONTH(date_month)
. Синтаксически они одинаковы, но EOMONTH - это Sargable , тогда как MONTH
и YEAR
- нет.
Затем несколько дополнительных ссылок / предложений, которые непосредственно связаны с изменениямиЯ сделал
- Хотя я убрал while lopp, не попадайтесь в ловушку, думая, что это лучше, чем курсор. Правильно объявленный курсор будет выполнять цикл while, как ваш - Плохие привычки к удару: Думать, что цикл WHILE не является курсором
- Общий консенсус заключается в том, чтопрефикс имен объектов не очень хорошая идея. Из контекста должно быть либо очевидно, является ли объект таблицей / представлением, либо функцией / процедурой, либо это не должно иметь значения, т. Е. Нет необходимости различать таблицу или представление, и на самом деле мы можем захотеть изменитьот одного к другому, поэтому наличие префикса делает вещи хуже, а не лучше.
- Среднее соотношение времени, потраченного на чтение кода, и времени, потраченного на написание кода, составляет около 10: 1 - поэтому стоит отформатироватьваш код, когда вы пишете его, чтобы его было легко читать. Это очень субъективно для SQL, и я бы не рекомендовал никаких конкретных соглашений, но я не могу поверить, что на секунду вы обнаружите, что ваш исходный код свободно распространяется и легко читается. Мне потребовалось около 10 минут, чтобы распутать первое заявление вставки.
РЕДАКТИРОВАТЬ
Вышеуказанное неверно, EOMONTH()
не может быть sargable, поэтому не работает лучше, чем YEAR(x) = YEAR(y) AND MONTH(x) = MONTH(y)
, хотяэто все еще немного проще. Если вам нужен действительно предикат sargable, вам нужно создать начальную и конечную дату, используя @tdate
, поэтому вы можете использовать:
DATEADD(MONTH, DATEDIFF(MONTH, '19000101', @tdate), '19000101')
, чтобы получить первый день месяца для @tdate
, затемпочти тот же форум, но добавьте месяцы к 1 февраля 1900 года, а не к 1 января, чтобы получить начало следующего месяца:
DATEADD(MONTH, DATEDIFF(MONTH, '19000201', @tdate), '19000201')
Итак, следующее:
DECLARE @Tdate DATE = '2019-10-11';
SELECT DATEADD(MONTH, DATEDIFF(MONTH, '19000101', @tdate), '19000101'),
DATEADD(MONTH, DATEDIFF(MONTH, '19000201', @tdate), '19000201');
Вернет 1-еОктябрь и 1 ноября соответственно. Поместив это обратно в исходный запрос, вы получите:
WHERE NOT EXISTS
( SELECT 1
FROM tbl_bill AS tb
WHERE date_month >= DATEADD(MONTH, DATEDIFF(MONTH, '19000101', @tdate), '19000101'),
AND date_month < DATEADD(MONTH, DATEDIFF(MONTH, '19000201', @tdate), '19000201')
AND tb.holdingNo = h.holdinNo
AND (tb.update_by IS NOT NULL OR tb.ispay=1)
);