Показаны сообщения с ярлыком Группы дат. Показать все сообщения
Показаны сообщения с ярлыком Группы дат. Показать все сообщения

понедельник, 16 июля 2012 г.

Цены и дни недели.

В этом посте я бы хотел поговорить о стандартах хранения цен и способах работы с такими стандартами. Представьте, что у вас есть услуга, цена на которую действует все лето. То есть с 1 июня по 31 августа. Разумно было бы хранить ее так.

CREATE TABLE [Costs](
  [id] int NOT NULL PRIMARY KEY
 ,[cost] money NOT NULL
 ,[begin] [smalldatetime] NOT NULL
 ,[end]  [smalldatetime] NOT NULL
);

GO

insert into [Costs] ([id],[cost],[begin],[end])
select 1, 500, convert(smalldatetime,'01.06.2012',104), convert(smalldatetime,'31.08.2012',104)

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

Теперь два раза в неделю цена на услугу будет изменять. Если мы будем действовать по старой схеме, вместо 1 записи указывающей цену на 3 месяца, придется заводить 2 записи на каждую неделю, которых в летние месяцы более 13(итого более 26 записей). Что довольно неприятно.

Во многих системах работающих в туристическом сегменте является практически стандартом хранения данных в следующем виде.

CREATE TABLE [Costs](
  [id] int NOT NULL PRIMARY KEY
 ,[cost] money NOT NULL
 ,[begin] [smalldatetime] NOT NULL
 ,[end]  [smalldatetime] NOT NULL
 ,[week] char(7) NOT NULL
);

GO

insert into [Costs] ([id],[cost],[begin],[end],[week])
select 1, 500, convert(smalldatetime,'01.06.2012',104), convert(smalldatetime,'31.08.2012',104), '....567'

union all 
select 2, 400, convert(smalldatetime,'01.06.2012',104), convert(smalldatetime,'31.08.2012',104), '1234...'

Поле week хранит дни недели, в которые действует эта цена. Каждый день недели от понедельника до воскресенья пронумерован от единицы до семи соответственно. Для i от 1 до 7, если на i-ой позиции стоит i, то цена действует в этот день недели, если ".", то нет.

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

четверг, 12 июля 2012 г.

Поиск подряд идущих дат (Островов)

Задача которая в том или ином виде встает перед многими. Как набор дат разбить на группы подряд идущих дней? Нередко для обозначения таких групп используется слово острова (Islands).
Итак, имеем таблицу с исходными данными.
CREATE TABLE [Data] (
[Date] [datetime] NOT NULL
);
GO
insert into [Data] ([Date])
      select convert(datetime,'01.04.2012',104)
union select convert(datetime,'02.04.2012',104)
union select convert(datetime,'31.03.2012',104)

union select convert(datetime,'09.05.2012',104)
union select convert(datetime,'10.05.2012',104)

union select convert(datetime,'31.12.2012',104)
union select convert(datetime,'01.01.2013',104)
И хотим в итоге получить следующие результат