Ето модел за постигане на заявеното от вас изискване.
Връзка към модела на данните от времевите сериисилно>
Връзка към нотация IDEF1X за тези, които не са запознати със стандарта за релационно моделиране.
-
Нормализирано до 5NF; без дублиращи се колони; без аномалии при актуализиране, без нулеви стойности.
-
Когато състоянието на продукт се промени, просто вмъкнете ред в ProductStatus с текущата DateTime. Няма нужда да докосвате предишните редове (които са били верни и остават верни). Няма фиктивни стойности, които инструментите за отчитане (различни от вашето приложение) трябва да интерпретират.
-
DateTime е действителната DateTime, на която Продуктът е бил поставен в това състояние; "От", ако щете. "До" се извлича лесно:това е Дата и час на следващия (DateTime> "От") ред за Продукта; където не съществува, стойността е текущата DateTime (използвайте ISNULL).
Първият модел е завършен; (ProductId, DateTime) е достатъчно, за да осигури уникалност за първичния ключ. Въпреки това, тъй като изисквате скорост за определени условия на заявка, ние можем да подобрим модела на физическо ниво и да предоставим:
-
Индекс (вече имаме PK Index, така че първо ще го подобрим, преди да добавим втори индекс), за да поддържа покрити заявки (тези, базирани на всяка подредба на { ProductId | DateTime | Status } могат да бъдат предоставени от индекса, без да за да отидете на редовете с данни). Което променя връзката Status::ProductStatus от Non-Identifying (прекъсната линия) на Identifying type (плътна линия).
-
Подредбата на PK се избира на базата на това, че повечето заявки ще бъдат времеви серии, въз основа на Product⇢DateTime⇢Status.
-
Вторият индекс се предоставя за подобряване на скоростта на заявките въз основа на състоянието.
-
При алтернативната подредба това е обърнато; т.е. ние най-вече искаме текущото състояние на всички продукти.
-
Във всички версии на ProductStatus колоната DateTime във вторичния индекс (не PK) е DESCending; най-новото е първият.
Предоставих дискусията, която поискахте. Разбира се, трябва да експериментирате с набор от данни с разумен размер и да вземете свои собствени решения. Ако тук има нещо, което не разбирате, моля, попитайте и аз ще разширя.
Отговори на коментари
Докладвайте всички продукти с текущо състояние 2
SELECT ProductId,
Description
FROM Product p,
ProductStatus ps
WHERE p.ProductId = ps.ProductId -- Join
AND StatusCode = 2 -- Request
AND DateTime = ( -- Current Status on the left ...
SELECT MAX(DateTime) -- Current Status row for outer Product
FROM ProductStatus ps_inner
WHERE p.ProductId = ps_inner.ProductId
)
-
ProductId
е индексиран, водещ колон, двете страни -
DateTime
в индексирано, 2-ро столче в опция за покрита заявка -
StatusCode
е индексирано, 3-та колона в опцията за покрита заявка -
Тъй като
StatusCode
в индекса е DESCending, е необходимо само едно извличане, за да се удовлетвори вътрешната заявка -
редовете се изискват едновременно за една заявка; те са близо един до друг (поради Clstered Index); почти винаги на една и съща страница поради късия размер на редовете.
Това е обикновен SQL, подзаявка, използваща силата на SQL машината, обработка на релационни набори. Това е един правилен метод , няма нищо по-бързо и всеки друг метод би бил по-бавен. Всеки инструмент за отчети ще създаде този код с няколко щраквания, без въвеждане.
Две дати в състоянието на продукта
Колони като DateTimeFrom и DateTimeTo са груби грешки. Нека го вземем по важност.
-
Това е груба грешка при нормализиране. "DateTimeTo" лесно се извлича от единичната DateTime на следващия ред; следователно е излишна, дублирана колона.
- Прецизността не влиза в това:това се разрешава лесно чрез DataType (DATE, DATETIME, SMALLDATETIME). Независимо дали показвате една секунда по-малко, микросекунда или наносекунда, е бизнес решение; няма нищо общо с данните, които се съхраняват.
-
Внедряването на колона DateTo е 100% дубликат (на DateTime на следващия ред). Това отнема двойно дисково пространство . За голяма маса това би било значителна ненужна загуба.
-
Като се има предвид, че това е къс ред, ще ви трябват два пъти повече логически и физически входно/изходни операции за да прочетете таблицата при всеки достъп.
-
Идвойно повече кеш пространство (или казано по друг начин, само наполовина по-малко редове биха се побрали във всяко дадено кеш пространство).
-
С въвеждането на дублирана колона вие сте въвели възможността за грешка (стойността вече може да бъде извлечена по два начина:от дублиращата се колона DateTimeTo или DateTimeFrom на следващия ред).
-
Това също е Аномалия при актуализиране . Когато актуализирате който и да е DateTimeFrom се актуализира, DateTimeTo от предишния ред трябва да бъде извлечен (няма голяма работа, тъй като е близо) и актуализиран (голямо значение, тъй като е допълнителен глагол, който може да бъде избегнат).
-
„По-кратки“ и „преки пътища за кодиране“ са без значение, SQL е тромав език за манипулиране на данни, но SQL е всичко, което имаме (Просто се справете с това). Всеки, който не може да кодира подзаявка, наистина не трябва да кодира. Всеки, който дублира колона, за да облекчи незначителни "трудности" при кодирането, наистина не трябва да моделира бази данни.
Забележете добре, че ако се запази правилото от най-висок ред (Нормализация), целият набор от проблеми от по-нисък ред се елиминира.
Мислете от гледна точка на набори
-
Всеки, който има „затруднение“ или изпитва „болка“ при писане на прост SQL, е осакатен при изпълнение на работната си функция. Обикновено разработчикът ене мислене от гледна точка на комплекти а релационната база данни е модел, ориентиран към набор .
-
За заявката по-горе се нуждаем от Current DateTime; тъй като ProductStatus е набор от състояния на продукти в хронологичен ред, просто ни трябва най-новото или MAX(DateTime) от набора принадлежащи към Продукта.
-
Сега нека разгледаме нещо, за което се твърди, че е "трудно", по отношение на комплекти . За отчет за продължителността, през която всеки продукт е бил в определено състояние:DateTimeFrom е налична колона и дефинира хоризонталната граница, поднабор (можем да изключим по-ранни редове); DateTimeTo е най-ранният от поднабора на състоянията на продукта.
SELECT ProductId,
Description,
[DateFrom] = DateTime,
[DateTo] = (
SELECT MIN(DateTime) -- earliest in subset
FROM ProductStatus ps_inner
WHERE p.ProductId = ps_inner.ProductId -- our Product
AND ps_inner.DateTime > ps.DateTime -- defines subset, cutoff
)
FROM Product p,
ProductStatus ps
WHERE p.ProductId = ps.ProductId
AND StatusCode = 2 -- Request
-
Мислете от гледна точка на получаване на следващия ред е ориентиран към ред, не набор-ориентирана обработка. Обезсилване, когато се работи с база данни, ориентирана към множество. Оставете оптимизатора да направи всичко това вместо вас. Проверете своя SHOWPLAN, това оптимизира прекрасно.
-
Неспособност за мислене в комплекти , като по този начин се ограничава до писане само на заявки от едно ниво, не е разумно оправдание за:внедряване на масивно дублиране и актуализиране на аномалии в базата данни; губене на онлайн ресурси и дисково пространство; гарантира половината от производителността. Много по-евтино е да се научите как да пишете прости SQL подзаявки за получаване на лесно извличани данни.