Разделы
Публикации
Популярные
Новые
Главная » Оптимизация производительности transact

1 2 3 4 5 6 7 ... 55

даты sZ.

задача коварнее, чем кажется: cyuieciByeT множество неправильных решении. Например, возникает искушение сделать нечто вроде

SELECT fname. Iname. hire date

FROM EMPLOYEE

WHERE MONTH(hire date)-MONTH(GETDAIE())

Ho это peiiieiHie не снособно обработать ситуацию, при которой тридцатидневный период мол<ет охватывать два или даже три месяца. Другое неправильное решение может быть получено, если попытаться синтезировать дату, используя текущий год, дату и месяц приема иа работу, как здесь:

SELECT fname. Iname. hire date

FROM EMPLOYEE

WHERE CAST(CAST(YEAR(GETDATE()) AS varcharC4))+

SbBSTRING(C0NVERT(char(8). h1re date.ll2).5.4) AS datetime) BETWEEN GETDATEO AND GETDATE()+30

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

В лучшем решении точная дата годовщины ие важна. Применяется функция DATEDIFFO SQL Server, чтобы сделать саму дату годовщины несущественной. DATEDIFFO возвращает разницу во времени между двумя датами, используя меру даты или времени, которую вы укажете. Данная функция принимает три параметра: часть даты или времени, в которой вы хотите получить разницу (например, дин, месяцы, минуты, часы), и две даты, для которых вы хотите вычислить разницу во времени. Вы можете указать любую часть даты, включая q или qq для календарных кварталов, так же как h, mi, ss и ms для временных частей. Вот код:

SELECT fname. Iname. h1re date

FROM EMPLOYEE

WHERE DATED!FF(yy. hire date.GETDATE()-30) > DATEDIFF(yy. hire date.GETDATE())

Этот код сообщает: если количество лет между датой ирие.ма и сегодняшней датой плюс 30 дней превышает количество лет между датой приема и сегодняшней датой, годовщина npnevia на работу должна быть в течение тридцати дней, независимо от 4>актической ее даты .

Обратите внимание на использование простой арифметики для добавления дней к переменной типа datetime (в данном случае результат функции GETDATEO). Вы можете добавлять или вычитать дни из переменных и полей типа datetime или smalldatetime с помощью простой арифметики. Также обратите вии.мание на применение функции GETDATE. Она возвращает значение текущей даты и вре-.мени.

Подобно DATEDIFFO, DATEADD() добавляет заданное количество единиц времени к переменной или столбцу типа datetime. Вы можете добавлять (и вычитать, используя отрицательные числа) все обычные компоненты даты, так же как кварталы и время. В случае целых дней синтаксически более компактно использовать простую ариф.метику, чем вызывать функцию DATEDIFFO, результаты будут те же самые.



функ1Т11Я DATEPARTO и функции YEAR(), MONTH() и DAY() извлекают части заданной даты. В дополненне к уже упо.мянуты.м частям DATEPART() молсет возвра-итать день недели, неделю года и день года как целое значение.

Даты и простая арифметика

По\ньмо возмолсности добавления или вычитания заданного числа дней с помощью простой арифметики, вы также можете вычитать одну дату из другой для того, чтобы определить количество дней между ними, но вы должны быть осторожны. SQL Server вернет количество дней между дву.мя дата-ми, однако, если любая из них содержит временную часть, сервер будет вынулсден включить нецелые дни в свои вычисления. Так как мы преобразуем результат к целому типу (без преобразования при вычитании одной даты из другой SQL Server вернет третью дату - это не слишком полезно), часть времени от двенадцати часов и больше будет считаться полны.м днем. Это несколько противоестественно. Нанри.мер, рассмотрим такой код:

SELECT CAST(GETDATE()-19940101 AS 1nt) Если результат GETDATE() равен 1999-01-17 20:47:40, SQL Server вернет

1843

Хотя, DATEDIFF(dd, GETDATE(),19940101) вернет 1842

Почему такое несоответствие? DATEDIFF() считает только полные дни, тогда как простая арифметика дат SQL Server считает также и нецелые дни. Проблема станет более очевидна, если мы преобразуем дату не к целому значению, а к значенито с плавающей запятой, как здесь:

SELECT CAST(GETDATE()-1994010r AS float) 1842.8664351851851

Итак, между 1 января 1994 года и 17 января 1999 года 20:47:40 1842,87 дней, или, если округлить до ближайшего целого, 1843 дня.

Чтобы эти два метода во.звращали одинаковый результат, мы должны изменить время первой даты на что-нибудь до полудня, нанри.мер так:

SELECT CAST(CAST(1999-01-17 11:47:40 AS datetime)-19940101 AS int)

Клиентам может не понравиться, что их данные изменяются просто ради приспособления к халтурному коду. Это все равно что делать операцию на сердце, вместо того чтобы починить сломанный стетоскоп. Лучше просто убрать время из вычислений, так как оно для нас не важно:

SELECT CAST(CAST(CDNVERT(char(8).GETDATE().112) AS datetime)-19940101 AS int) Данный метод преобразует дату к символьной строке из вось.ми 6aiiT0B, а затем обратно, чтобы избавиться от временной части. После этого время у обеих дат будет равно 00:00:00.000, тем самым мы исключаем возможность искажения результатов неполными днями.

Определение временных интервалов

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



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

CREATE TABLE timeclock (Employee varcharOO).

Timelri smalldatetime.

TimeOut smalldatetime

INSERT timecloek VALUES(Pythia.07:31:3412:04:01) INSERT timeclock VALUES(Pythia.12:45:10.17:32:49) INSERT timeclock VALUES(Dionysus.9:31:29.10:46:55) INSERT timeclock VALUES(Dionysus,10:59:32.11:39;12) INSERT timeclock VALUESCDionysus,13:05:16.14:07:41) INSERT timeclock VALUES(Dionysus,14:11:49.14:57:02) INSERT timeclock VALUESCDionysus.15:04:12,15:08:38) INSERT timeclock VALUESCDionysus.15:10:31.16:13:58) INSERT timeclock VALUESCDionysus.16:18:24.16:58:01)

Похол^е на то, что Pythia - сознательный работник, тогда как Dionysus, похоже, немного бездельничает. Запрос для определения, сколько минут каждый сотрудник тратит па перерывы, .может выглядеть подобно приведенному ниже; SELECT tl,Employee.

DATEADD(mi.l.tl.TimeOut) AS StartOfLoafing, DATEADD(mi.-l,t2.TimeIn) AS EndOfLoafing. DATEDIFF(mi.tl,TimeOut.t2.TimeIn) AS LengthOfLoafing FROM timeclock tl JOIN timeclock t2 ON (tl .F,mployee=t2,Employee) WHERE (DATEADD(mi,l.tl.TimeOut) <= DATEADD(mi,-1,t2,TimeIn))

Employee

StartOfLoafing

EndOfLoafing

LengthOfLoafing

Pytnia

1900-01-01

1900-01-01

44:00

Dionysus

1900-01-01

1900-01-01

59:00

Dionysus

1900-01-01

1900-01-01

04:00

Dionysus

1900-01-01

1900-01-01

04:00

Dionysus

1900-01-01

1900-01-01

11:00

Dionysus

1900-01-01

1900-01-01

11:00

Dionysus

1900-01-01

1900-01-01

11:00

Dionysus

1900-01-01

1900-01-01

03:00

Dionysus

1900-01-01

1900-01-01

03:00

Dionysus

1900-01-01

1900-01-01

03:00

Dionysus

1900-01-01

1900-01-01

03:00

Dionysus

1900-01-01

1900-01-01

10:00

Dionysus

1900-01-01

1900-01-01

10:00

Dionysus

1900-01-01

1900-01-01

10:00

Dionysus

1900-01-01

1900-01-01

10:00

Dionysus

1900-01-01

1900-01-01

10:00

Dionysus

1900-01-01

1900-01-01

17:00

Dionysus

1900-01-01

1900-01-01

17:00

Dionysus

1900-01-01

1900-01-01

17:00

Dionysus

1900-01-01

1900-01-01

17:00

Dionysus

1900-01-01

1900-01-01

17:00

Dionysus

1900-01-01

1900-01-01

17:00



Очевидно, здесь слишком .\шого перерывов - даже Dionysus не может иметь больше перерывов, чем все рабочее время. Вводит в заблуждение то, что первая запись выглядит правильной - похоже, что у Pythia сорок одну минуту длился ланч. Но проблемы начинаются, как только для данного работника находится более двух нар. В дополнение к правильному вычислению времени .между пе-риода.\пг работы Dionysus запрос вычисляет разницу в .хгннутах .между временем прихода и временем ухода, которые не соответствуют друг другу. В.место этого .мы должны вычислять каждый перерыв, основываясь на ближайшем времени ухода, как здесь:

SELECT tl.Empoyee,

DATEADDdni.l.tl.TimeOut) AS StartOfLcafinc.

DATEADD(mi.-:.t2.TimeIn) AS EndOfLoafing,

DATEDIFF(m1,tl,Time0ut,t2,TimeIn) AS LengthOfLoafing FROM timeclock Tl JOIN timeciock T2 ON (tl,Employee=t2,Employee) WHERE (DATEADD(mi,l.tI.TimeOut)=

(SELECT MAX(DATEADD(mi.l.tJ.TimeOut))

FROM timeclock T3

WHERE (t3,Employee=tl.Employee)

AND (DATEADD(m1.].t3.Time0ut) <= DATEADU(mi,л.t2.Timein))))

Employee StartOfLoafing EndOfLoafing LengthOfLoafmg

Pythia 1900-01-01 12:05:00 1900-01-01 12:44-00 41 Dionysus 1900-01-01 10:48:00 1900-01-01 10:59:00 13 Dionysus 1900-01-01 11:40:00 1900-01-01 13:04:00 86 Dionysus 1900-01-01 14:09:00 1900-01-01 14:11:00 4 Dionysus 1900-01-01 14:58:00 1900-01-CI 15:03:00 7 Dionysus 1900-01-01 15:10:00 1900-01-01 15;]0:00 2 Dionysus 1900-01-01 16:15:00 1900-01-01 16:17:00 4

Обратите внимание иа использование коррелированного подзапроса для определения ближайшего времени ухода. Он является коррелированным, так как и ограничивает данные вненшего .запроса, и ограничен и.ми. Для каждой записи из Т1 значение столбца Employee передается подзапросу в качестве пара.метра, и подзапрос выполняется заново. Са.ма запись потом либо включается, либо исключается из результирующего множества, в зависимости от того, больше или нет ее значение TimeOut но сравиетнно с тем, которое вернул подзапрос. Таким образом, коррелированные подзапросы и их главные запросы зависят друг от друга.

Получен}ше результтгрующее множество на треть меньше полученного в результате работы первого .запроса. Теперь перерывы Dionysus кажутся если и пе более обоснованными, то уж точно более нравдонодобны.ми.

Вы можете легко расширить этот запрос, чтобы он возвращал 1громежуточ-ные итоги для каждого сотрудника, исноушзуя расширеше COMPUTE Transact-SQL, как здесь:

SEtECT tl.Employee.

DATEADD(mi.l.tl.TimeOut) AS StartOfLoafing. DATEADD(mi.-l.t2.TimeIn) AS EndOfLoafing. DATEDIFF(mi.tl.TimeOut,t2,TimeIn) AS LengthOfLoafmg FROM timeclock Tl JOIN timeclock T2 ON (tl,Employee=t2,Employee) WHERE (DATEADD(mi,l.tl,TimeOut)=

(SELECT MAX(DATEADD(mi,l,t3.TimeOut)) FROM timeclock T3



.-.HLKE . t3. Enip i oyec-c 1. hnp; oyee;

AND (DAEACOdni ,l,c3.Tirri0Cui; DATcADD(ni) . 1. l2. шщ1п;);) ORDER Bv tl.Employee

COMPLIIE SUM(DA EDlFF(.-iii .tl/4meOjt,t2.Tirr,e:n)) BY li.Employee

tmployee StartOfLcafing

ElCGfLcaflng

LcngthOfLoafing

Dionysus Dionysus Dionysus Dionysus Dionysus Dionysus

190C-01-0! 20:48

1900-01-01 11:40

1900-01-01 14:09

1900-01-01 14:58

1900-01-01 15:10

1900-01-01 16:15

00 1900-01-0; 11:01:00 13

00 1900-01-01 13:06:00 86

00 1900-01-01 14:13:00 4

00 1900-01-01 15:05:00 7

00 1900-01-01 15:12:00 2

00 1900-01-01 16:19:00 4

Sum 116

PythlG 1900-01-01 12:05:00 1900-01-01 12:46:00 41

116 41

Обратите внимание на иа.Ч1ин1е предложения ORDER BY - это требование COMPUTE BY. COMPUTE позволяет сгенерировать элементарные итоговые значения для результирующего .множества. COMPUTE BY - это вариант COMPUTE, который позво-шет указать фуппируе.мые столбцьь Это весьма шбкое средство, так как с его помощью него можно генерировать агрегатные значения, которых нет в списке SELECT, и группировать по столбца.м, отсутствуюпщм в предложении GROUP BY. Одна серьезная проблема этого метода - для одного запроса генерируется множество результатов: одни для каждой группы и один для каждого множества итоговых значен1иг группы. Больщинство юшептских приложен1ГЙ не умеет работать с итогами, вычисляе.мы.ми COMPUTE. Вот почему Microsoft с недавних пор не одобряет ее использование и рекомендует вместо нее применять расширение ROLLUP предложения GROUP BY. Вот запрос с COMPUTE, переписанный с применением ROLLUP:

SELECT ISNULL(tl.Employee.Total) AS Employee.

DATEADD(mi,l.tl,TimeOut) AS StartOfLoafing.

DATEADD(mi.-l.t2,TimeIn) AS EndOfLoafing.

SUM(DATEDIFF(mi.tl.TimeOut.t2.Time!n)) AS LengthOfLoafing FROM timeclock Tl JOIN timeclock T2 OK (tl,Employee=t2.Employee) WHERE (DATEADD(m1.l.tl.TimeOut)=

(SELECT MAX(DATEADD(mi,l.t3.TimeOut))

FROM timeclock T3

WHERE (t3,Employee=tl.Employee)

AND (DATEADD(mi.l.t3,TimeOut) <= DATEADD(mi,-1.t2.Time!n)))) GROUP BY tl.Employee.

DATEADD(mi,l,tl.TimeOut),

DATEADD(mi.-l.t2.TimeIn).

DATEDIFF(m1.tl,TimeOut.t2,TimeIn) WITH ROLLUP HAVING ((GROUPING(DATEADD(mi ,-l.t2.TimeIn))=0)



CR (GRCUPING (DATEADD (nil. 1.11 Т i meOut)) *GRCU? i MG (DA FtADD (ipi . - i . 12. T i Tie I n); =2))

LengthOfLoafIng

Employee

StartOfLoafing

EndOfLoafing

Lenc

Dionysus

1900-01

1900-01-01

59:00

Dionysus

1900-01

1900-01-01

59:00

Dionysus

1900-01

1900-01-01

04:00

Dionysus

1900-01

1900-01-01

04:00

Dionysus

1900-01

1900-01-01

11:00

Dionysus

1900-01

1900-01-01

11:00

Dionysus

1900-01

1900-01-01

03:00

Dionysus

1900-01

1900-01-01

03:00

Dionysus

1900-01

1900-01-01

10:00

Dionysus

1900-01

1900-01-01

10:00

Dionysus

1900-01

1900-01-0;

17:00

Dionysus

1900-01

1900-01-01

17:00

Dionysus

NULL

NULL

Pythia

1900-01

12-05

190C-01-OJ

44:00

Pythia

1900-01

12:05

1900-01-01

12:44:00

Pythia

NULL

NULL

***Total***

NULL

NULL

Как видите, запрос получился намного длиннее. Увеличение эффективности но вре.мя выполнения получено ценой компактности синтаксиса.

При использовании WITH ROLLUP в результирующее множество добав.тяются дополш1тельные заииси, содержащие нро.мелтючные итоги для каждого столбца, указанного в предложении GROUP BY. В отличие от запроса с COMPUTE, этот запрос возвращает только одно результирующее множество. Нас не интересуют все сгенерированные итоги, так что мы применяем предложение HAVING для удаления всех итоговых записей, кроме про.межуточных итогов для сотрудников и общего итога отчета. Первый набор NULL-значений в результирующем множестве соответствует промежуточному ИТ017 для Dionysus. Второй набор - про.ме-жуточные итоги Pythia. Третий набор означает общий итог для результирующего множества.

Обратите внимание на использование функции GROUPING() для создания специальной строки для итогов отчета и для офаничения записей, которые попадут в результирующее множество. GR0UPING() возвращает 1, когда указанный столбец группируется в пределах записи результирующего множества, и О - когда нет. Сгруппированные столбцы возвращаются в результирующее множество как NULL. Если в самих ваших данных нет NULL, вы можете задействовать ISNULL() для тех же целей, что и GR0UPING(), так как только сгруппированные столбцы будут равны NULL.

Создание календарей

Поля типа datetime применяются для создания календарей и расписаний. Рас-с\ютрим следующую задачу: библиотеке необходимо вычислить точный день, когда читатель должен вернуть книгу, чтобы избежать штрафа. Обычно это будет четырнадцатый календарный день с того мо.мента, кода читатель взял книгу, но поскольку библиотека закрыта в выходные и праздничные дни, проблема на самом деле сложнее, чем кажется. Давайте начнем с создания простой таблицы, которая будет содержать нерабочие дни библиотеки. Будет достаточно таблицы



с двумя столбцами: HolidayName и HolidayDate. Заполним се названиями и датами каждого нерабочего дня библиотеки. Вот код для создания этой таблицы:

USE tempcib GC

DROP TABLE HOLIDAYS

CREATE TABLE HOLIDAYS (HoiicayNdine varchar(3G;. Hol-dayOate smalldatetime)

INSERT HOLIDAYS VALUESCNew Years Day . 1999C101 )

INSERT HCLID.AYS VALUESCVaientlnes Day . 19990214 )

INSERT HOLIDAYS VALUESCSt. Patricks Day , 19990317 )

INSERT HOLIDAYS VALUES( Memorial Day . 19990530 )

INSERT HOLIDAYS VALUES( independence Day . 19990704 )

INSERT HOLIDAYS VALUES( LaDor Day . 19990906 )

INSERT HOLIDAYS VALUES( Indigenous Peoples Day . 19991011 )

INSERT HOLIDAYS VALUES( Halloween . 19991031 )

INSERT HOLIDAYS VALUES( Thanksg1ving Day , 19991125 )

INSERT HOLIDAYS VALUES( Day After Thanksgiving . 19991126 )

INSERT HOLIDAYS VALUES( Christmas Day . 19991225 )

INSERT HOLIDAYS VALUES( New Years Eve , 1Э99123Г')

SELECT * FROM HOLIDAYS

HoldayName

HolidayDate

New Years Day

1999-01-01 00

Valentines Day

1999-02-14 00

St, Patricks Day

1999-03-17 00

Memorial Day

1999-05-30 00

Independence Day

1999-07-04 00

Labor Day

1999-09-06 00

Inaigenous Peoples Day

1999-10-11 00

Halloween

1999-10-31 00

Thanksgiving Day

1999-11-25 00

Day After Thanksgiving

1999-11-26 00

Christmas Day

1999-12-25 00

New Years Eve

1999-12-31 00

Зате.м создади.\г таблицу с датами выдачи и возврата для целого года. Она также будет состоять из двух столбцов, ClieckOutDate и DueDate. Для того чтобы создать таблицу, заполним столбец Cliecl<OutDate кажды.м дне.м в году и DueDate кажды.м дне.м плюс четыр)1адцать катендарных дней. Храни.мые процедуры - откомпилированные нрогра.м.мы SQL, которые нагю.минают ЗОЕ'-процедуры или подпрограммы - хорошо для этого приспособлены за счет наличия локальных переменных и операторов управления ходо.м выполнения (напри.мер, кон-струкцшг циклов). Вы можете исполыювать локальные переменные и операторы управления ходо.м выполнения и вне хранимых процедур, но ими сложно управлять, и вы не сможете задействовать всю .мощь языка. Вот процедура, которая создает и заполняет таблицу DUEDATES:

USE tempdb GO

DROP TABLE DUEDATES GO

CREATE TABLE DUEDATES (CheckOLitDate smalldatetime. DueDate smalldatetime)

Языки програ.ммиронаиия третьего поколения. - Пргшеч. перев.



DROP PROC popduedales GO

CREATE PROCEDURE popduedates AS SET NOCOUNT .ON

DECLARE (Pyear iniege . insertdcy Galellire

SELECT (ayear=YEAR(GErCATE()), Cairiserlday=CAST(eyed- AS char(4) ; + 010Г TRUNCATE TABLE DUEDATES -- Для того случая, если данная процедура будет

--запущена более одного раза (запускать только из tenpdb) WHILE YEAR((ainsertday)>(ayear BEGIN

-- He вставлять выхсднью или праздники,

-- так как библиотека закрыта

IF ((SELECT DATEPART(aw.@insertday)) NOT IN (i.7);

AND NOT EXISTS (SELECI * FROM HOLIDAYS WHERE riol;dayDate=@insertday) INSERT DUEDATES VALUES ((ainsertday. @insertday+14)

SET (ainsertday=(ainsertday-l

EXEC popduedates

Теперь, когда мы создали таблицу, нужно скорректировать каждую дату возврата, которая выпадает на праздник или выходные путем замены на следующую корректную дату. Задача значительно упростилась, учитывая тот факт, что в таблице нет дней возврата, совпадающих с выходны.ми и.ти праздниками. Так как даты выдачи и возврата обычно разделены четырнадцатью календарны.ми днями, единственный вариант, когда дата возврата может вьнтасть на выходные (после начальной инициа7Н1зации таблтщы), - изменение даты возврата, выпадающей на праздники, на дату возврата, выпадающую иа выходные, - то есть сделаем это сами.

Возможньпй подход к решению этой задачи заключается в вьшолнении трех операторов UPDATE: один для перемещения на следующтн ! день дат возврата, которые выпадают на выходные, один для перемещения суббот на понедельники и один для перемещения воскресений на понедельники. Мы должны выполнять эти три оператора, пока не закончатся записи, на которые они могут воздействовать. Вот пример:

CREATE PROCEDURE fixduedates AS

SET NOCOUNT ON

DECLARE (akeepgoing integer

SET (akeepgoing=l

WHILE (@keepgoing<>0) BEGIN

UPDATE #DUEDATES SET DateDue=DateDue+l

WHERE DateDue IN (SELECT HolidayDate FROM HOLIDAYS)

SET (akeepgoing=(a(aR0WC0UNT

UPDATE #DUEDAT£S SET DateDue=DateDue-b2

WHERE DATEPART(dw.DateDue)=7

SET (akeepgol ng=(akeepgoing-i-(aiaROWCOUNT

UPDATE #DU£DAT£S SET DateDue-DateDue+1

WHERE DATEPART(dw.DateDue)=l

SET (akeepgoi ng=(cikeepgoi ng+(a(aROWCOUNT

Данный способ использует соединешю с таблтщей HOLIDAYS для изменения дат возврата, выпадающих на праздники, и функцию DATEPART() для корректировки дат возврата, выпадающих на выходные. После того как вы запустите



процедуру, у вас останется lao.nma с дата.чш выдачи и соответствующилт и.м датами возврата. Обратите внимание на псиользованпе (©(SROWCOUNT в хранимой процедуре для определения количества записей, затронутых каждым оператором UPDATE, Это позволяет определить, когда необходимо заканчивать цикл: тогда, когда ни олпн из трех операторов UPDATE пе затронет таблицу. Потребность в переменно!! (Skeepgoing !!лл10стр!!рует необходи.мость в Transact-SQL конструкции цикла DO... UNTIL НЛ1! REPEAT... UNTIL. Есл1! бы язык поддерживает сиитаксг!ческую конструкцию цикла, которая проверяла бы условие цикла в конце, а не в начале, мы .могли бы избав!!ться от переменной @keepgoing.

Получив достаточно !Ш!Ц!1 для ра:)мы!илений, .мы обычно .можем приду.мать лучшее решение такой итеративной задач!!, че.м то, которое сначала пришло на у.\ь и эта проблема ие исключение. Вот решен1!е этой задачи, в котором при.ме-няется только один 01!ератор UPDATE:

CREATE PROCEDURE fixduedates2 AS

SET NOCOUNT ON

SELECT Fixing DUEDATES -- Seed K<iRCWCOUNT WHILE {(aiaROWCOUNToQ) BEGIN UPDATE DUEDATES

SET DueDate=DueOate+CASE WHEN DATEFART(dw.DueDate)=6 THEN 3 ELSE 1 END WHERE DueDate IN (SELECT HolidayDate FROM HOLIDAYS)

Этот способ использует тот факт, что с самого начала в таблице нет дат возврата, пь!пада!ощих на п!5!ход!1Ь!е, и просто не со:)дает их, когда корректирует даты возврата, пыпада!0щие на праздники. Это осуществляется с помощью функции CASE. Если дата возврата, которую .мы собираемся скорректировать, уже выпадает иа пятницу, .мы просто не добавляем к 1!ей один день, так как последующие операторы UPDATE скорректируют ее, - .мы добавляем достаточно д!!ей, чтобы передвинуть ее на следующий понедельник. Конечно, эти.м мы не обработаем два праздн1!ка, случа!ощпеся одн!! за другим в четверг и пятницу, так что нам необходимо повторить процесс.

Эта процедура испол1,зует 1штереспу!0 технику возврата текстового сообщения для иницигшизации авто.матической переменной (a)@ROWCOUNT. В допол-не1!ие к 0!!опе1цению пользователя о то.м, что она будет делать, возврат строки устанавливает начальное значе!1ие (©(SROWCOUNT, равное 1 (потому что возвращается одна запись ), разрешая вход в цикл. Зате.м, внутри, успех или неудача оператора UPDATE устанавливают значе!1ие (a)@ROWCOUNT. Такой подход избавляет нас от необходимости применять вторую переме1!ну10-счетчик, такую как @@keepgoing. И снопа ко!!Струкция цикла с проверкой в ко!!це была бы здесь очень удобна.

И!югда на.м кажется, что на1 1дено на!1лучщее решеп1!е, но датьнейшее рассмотрение проблемы часто позволяет найти более оптимальный способ решения. 01!ти.миза!!ия SQL-запросоп - это итеративный процесс, требую!ций большого терпения. Вы должи!! научиться урапнопеи!Ипать выгоды, которые вы получаете, с усилия.ми, с которы.ми они вам обходятся. Уменыиение на несколько секунд времени п1>1полне!1ия запроса, который нужен один раз в день, не стоит вашего внимания, но небольшое ускорение выпол11ен1!я за!!роса, который выполняется тысячу раз, возможно, стоит. Умение прини.мать ре!1!ения, что оптимизировать, а что нет и какие при зтолг вносить !13.меиения, оттачивается года.ми.



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

CREATE PROCEDURE fixduedates3 AS SET NOCOUNT ON

UPDATE DUEDATES SET DbeDate=DueDate*

CASE WHEN (DATEPART{dw.DueDate)=6) THEN 3 WHEN (DATEPART(dw,DueDate)-5) AND EXISTS

(SELECT HolidayDate FROM HOLIDAYS WHERE HoiiGayDate-DueDate+1) THEN 4 ELSE 1 END

FROM HOLIDAYS WHERE DueDate = HolidayDate

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

В этой процедуре мы также избавились от подзапроса, применявшегося в пре-дьщуших способах. Transact-SQL поддерживает раснитрение FROM оператора UPDATE ANSI/ISO, которое позволяет модифтщировать одну таблицу, основываясь на данных из другой. В этом способе мы просто соедитнтли таблицы DUEDATES и HOLIDAYS, чтобы ограничить модифицируемые записи те.ми, даты которых найдены в таблице HOLIDAYS.

Строковые переменные и поля в SQL Server - самые простые типы. Подд,ержи-ваются строковые типы постоянной и переменной длины, оба этих типа ограничены 8000 байт. Как и переменные других типов, строковые переменные объявляются посредство ко.манды DECLARE:

DECLARE (avocalist char(20) DECLARE (asong varcharOO)

Строковые переменные при объявлении инициа.И13пруются значением NULL, значение им можно присвоить с помощью SET или SELECT вот так:

SET (aVocalist-Paul Rodgers SELECT (aSong=All Right Now

Конкатенация

Вы можете выполнить конкатенацию строк, используя оператор +, как здесь: SELECT @\/oca11st+ sang the classic +@Song+ for- the band Free



1 2 3 4 5 6 7 ... 55
© 2004-2024 AVTK.RU. Поддержка сайта: +7 495 7950139 в тональном режиме 271761
Копирование материалов разрешено при условии активной ссылки.
Яндекс.Метрика