sql >> Databáze >  >> RDS >> Access

Hlášení podrobněji než obvykle – Microsoft Access

Vykazování podrobněji než obvykle – Microsoft Access

Když vytváříme přehledy, obvykle to děláme s vyšší úrovní podrobnosti. Klienti například běžně chtějí měsíční přehled tržeb. Databáze by ukládala jednotlivé prodeje jako jeden záznam, takže není problém sčítat čísla za každý měsíc. Stejně jako rok nebo dokonce přechod z podkategorie do kategorie.

Předpokládejme však, že musí jít dolů ? Pravděpodobnější bude odpověď:„Návrh databáze není dobrý. zrušit a začít znovu!" Koneckonců, mít správná granularita pro vaše data je zásadní pro solidní databázi. Ale to nebyl případ, kdy normalizace neproběhla. Uvažujme o potřebě provést účet o zásobách a výnosech a nakládat s nimi způsobem FIFO. Rychle ustoupím a podotknu, že nejsem CBA a že jakákoli účetní tvrzení, která uvedu, je třeba brát s maximálním podezřením. V případě pochybností zavolejte svému účetnímu.

S vyloučením odpovědnosti se podívejme, jak aktuálně data uchováváme. V tomto příkladu potřebujeme zaznamenat nákupy produktů a poté musíme zaznamenat prodeje nákupů, které jsme právě koupili.

Předpokládejme, že pro jeden produkt máme 3 nákupy:
Date | Qty | Per-Cost
9/03 | 3 | $45
9/08 | 6 | $40
9/09 | 8 | $50

Tyto produkty později prodáváme při různých příležitostech za jinou cenu:
Date | Qty | Per-Price
9/05 | 2 | $60
9/07 | 1 | $55
9/10 | 4 | $50
9/12 | 3 | $60
9/15 | 3 | $65
9/19 | 4 | $55

Upozorňujeme, že podrobnosti jsou na úrovni transakce – pro každý nákup a pro každou objednávku vytváříme jeden záznam. To je velmi běžné a dává to logický smysl – stačí zadat množství produktů, které jsme prodali, za konkrétní cenu pro konkrétní transakci.

OK, kde jsou účetní věci, které jste odmítli?

Pro zprávy musíme vypočítat tržby, které jsme dosáhli na každou jednotku produktu. Říkají mi, že musí produkt zpracovat způsobem FIFO... to znamená, že první jednotka produktu, která byla zakoupena, by měla být první jednotkou produktu, která se má objednat. Abychom pak vypočítali marži, kterou jsme vytvořili na této jednotce produktu, musíme vyhledat cenu této konkrétní jednotky produktu a poté odečíst z ceny, za kterou byla objednána.

Hrubá marže =výnos z produktu – náklady na produkt

Nic světoborného, ​​ale počkejte, podívejte se na nákupy a objednávky! Měli jsme pouze 3 nákupy se 3 různými nákladovými body, poté jsme měli 6 objednávek se 3 odlišnými cenovými body. Který nákladový bod se tedy vztahuje ke kterému cenovému bodu?

Tento jednoduchý vzorec pro výpočet hrubé marže způsobem FIFO nyní vyžaduje, abychom přešli na granularitu jednotlivých jednotek produktu. V naší databázi nikde nic nemáme. Představuji si, že kdybych navrhl, aby uživatelé zadali jeden záznam na jednotku produktu, došlo by k poměrně hlasitému protestu a možná k nějakému osočování. Takže, co dělat?

Rozebrat to

Řekněme, že pro účetní účely použijeme datum nákupu k třídění každé jednotlivé jednotky produktu. Takto by to mělo vyjít:
Line # | Purch Date | Order Date | Per-Cost | Per-Price
1 | 9/03 | 9/05 | $45 | $60
2 | 9/03 | 9/05 | $45 | $60
3 | 9/03 | 9/07 | $45 | $55
4 | 9/08 | 9/10 | $40 | $50
5 | 9/08 | 9/10 | $40 | $50
6 | 9/08 | 9/10 | $40 | $50
7 | 9/08 | 9/10 | $40 | $50
8 | 9/08 | 9/12 | $40 | $60
9 | 9/08 | 9/12 | $40 | $60
10 | 9/09 | 9/12 | $50 | $60
11 | 9/09 | 9/15 | $50 | $65
12 | 9/09 | 9/15 | $50 | $65
13 | 9/09 | 9/15 | $50 | $65
14 | 9/09 | 9/19 | $50 | $55
15 | 9/09 | 9/19 | $50 | $55
16 | 9/09 | 9/19 | $50 | $55
17 | 9/09 | 9/19 | $50 | $55

Pokud si prostudujete rozpis, můžete vidět, že dochází k překrývání, kdy spotřebujeme nějaký produkt z jednoho nákupu na různé objednávky, zatímco jindy máme objednávku, která je splněna různými nákupy.

Jak bylo uvedeno dříve, ve skutečnosti nemáme těchto 17 řádků nikde v databázi. Máme pouze 3 řádky nákupů a 6 řádků objednávek. Jak získáme 17 řádků z obou tabulek?

Přidání dalšího bahna

Ale nekončíme. Právě jsem vám uvedl idealizovaný příklad, kdy jsme náhodou měli perfektní rovnováhu 17 zakoupených jednotek, což je kompenzováno 17 jednotkami objednávek na stejný produkt. V reálném životě to tak krásné není. Někdy nám zůstanou přebytečné produkty. V závislosti na obchodním modelu může být také možné držet více objednávek, než kolik je k dispozici v inventáři. Ti, kteří hrají na akciovém trhu, uznávají jako short-selling.

Možnost nerovnováhy je také důvodem, proč nemůžeme jednoduše sečíst všechny náklady a ceny a poté odečíst, abychom získali marži. Pokud nám zbylo X jednotek, potřebujeme pro výpočet zásob vědět, o jaký nákladový bod se jedná. Stejně tak nemůžeme předpokládat, že nevyřízená objednávka bude úhledně vyřízena jediným nákupem s jedním nákladovým bodem. Takže výpočty, které přicházíme, musí fungovat nejen pro ideální příklad, ale také pro případy, kdy máme přebytečné zásoby nebo nevyřízené objednávky.

Nejprve se zabývejme otázkou, kolik init produktu musíme vzít v úvahu. Je zřejmé, že jednoduchý SUM() množství objednaných jednotek nebo množství zakoupených jednotek nebude stačit. Ne, spíše musíme SUM() jak množství zakoupených produktů, tak množství objednaných produktů. Poté porovnáme SUM() a vybereme vyšší. Mohli bychom začít tímto dotazem:
WITH ProductPurchaseCount AS (
SELECT
p.ProductID,
SUM(p.QtyBought) AS TotalPurchases
FROM dbo.tblProductPurchase AS p
GROUP BY p.ProductID
), ProductOrderCount AS (
SELECT
o.ProductID,
SUM(o.QtySold) AS TotalOrders
FROM dbo.tblProductOrder AS o
GROUP BY o.ProductID
)
SELECT
p.ProductID,
IIF(ISNULL(pc.TotalPurchases, 0) > ISNULL(oc.TotalOrders, 0), pc.TotalPurchases, oc.TotalOrders) AS ProductTransactionCount
FROM dbo.tblProduct AS p
LEFT JOIN ProductPurchaseCount AS pc
ON p.ProductID = pc.ProductID
LEFT JOIN ProductOrderCount AS oc
ON p.ProductID = oc.ProductID
WHERE NOT (pc.TotalPurchases IS NULL AND oc.TotalOrders IS NULL);

To, co zde děláme, je, že se rozdělíme do 3 logických kroků:

a) získat SUM() množství zakoupených podle produktů
b) získat SUM() množství objednaných podle produktů

Protože nevíme, zda můžeme mít produkt, který může mít nějaké nákupy, ale žádné objednávky, nebo produkt, který má zadané objednávky, ale nemáme žádné nakoupené, nemůžeme opustit ani jeden ze dvou stolů. Z tohoto důvodu používáme tabulky produktů jako autoritativní zdroj všech ProductID, o kterých chceme vědět, což nás přivádí ke 3. kroku:

c) přiřaďte částky k jejich produktům, zjistěte, zda má produkt nějakou transakci (např. nákupy nebo objednávky), a pokud ano, vyberte vyšší číslo z páru. To je náš počet celkových transakcí, které produkt provedl.

Ale proč se transakce počítá?

Cílem je zjistit, kolik řádků musíme vygenerovat na produkt, abychom adekvátně reprezentovali každou jednotlivou jednotku produktu, který se účastnil buď nákupu nebo objednávky. Pamatujte si, že v našem prvním ideálním příkladu jsme měli 3 nákupy a 6 objednávek, což se obě vyrovnalo na celkem 17 jednotek zakoupených a následně objednaných produktů. Pro tento konkrétní produkt budeme muset být schopni vytvořit 17 řádků, abychom vygenerovali data, která jsme měli na obrázku výše.

Jak tedy převedeme jedinou hodnotu 17 v řadě na 17 řádků? Zde vstupuje kouzlo tabulky sčítání.

Pokud jste ještě neslyšeli o tabulce sčítání, měli byste teď. Nechám ostatní, aby vám doplnili předmět tabulky sčítání; tady, tady a tady. Stačí říci, že je to impozantní nástroj, který můžete mít ve své sadě nástrojů SQL.

Za předpokladu, že revidujeme výše uvedený dotaz tak, že poslední část je nyní CTE s názvem ProductTransactionCount, můžeme dotaz napsat takto:
<the 3 CTEs from previous exampe>
INSERT INTO tblProductTransactionStaging (
ProductID,
TransactionNumber
)
SELECT
c.ProductID,
t.Num AS TransactionNumber
FROM ProductTransactionCount AS c
INNER JOIN dbo.tblTally AS t
ON c.TransactionCount >= t.Num;

A pesto! Nyní máme tolik řádků, kolik budeme potřebovat – přesně – pro každý produkt, který potřebujeme k účtování. Všimněte si výrazu v klauzuli ON – děláme trojúhelníkové spojení – nepoužíváme obvyklý operátor rovnosti, protože chceme z ničeho nic vygenerovat 17 řádků. Všimněte si, že stejné věci lze dosáhnout pomocí CROSS JOIN a klauzule WHERE. Experimentujte s oběma, abyste zjistili, co funguje lépe.

Aby naše transakce platila

Máme tedy v naší dočasné tabulce nastavený správný počet řádků. Nyní potřebujeme naplnit tabulku údaji o nákupech a objednávkách. Jak vidíte na obrázku, musíme být schopni seřadit nákupy a objednávky podle data, kdy byly zakoupeny nebo objednány. A to je místo, kde přichází na pomoc ROW_NUMBER() a tabulka sčítání.
SELECT
p.ProductID,
ROW_NUMBER() OVER (PARTITION BY p.ProductID ORDER BY p.PurchaseDate, p.PurchaseID) AS TransactionNumber,
p.PurchaseDate,
p.CostPer
FROM dbo.tblProductPurchase AS p
INNER JOIN dbo.tblTally AS t
ON p.QtyBought >= t.Num;

Možná se divíte, proč potřebujeme ROW_NUMBER(), když můžeme použít sloupec Num. Odpověď zní, že pokud dojde k více nákupům, počet se zvýší pouze na množství tohoto nákupu, ale musíme dosáhnout až 17 – celkem 3 samostatné nákupy po 3, 6 a 8 jednotkách. Dělíme tedy rozdělení podle ProductID, zatímco číslo tally lze říci, že je rozděleno podle PurchaseID, což není to, co chceme.

Pokud jste spustili SQL, získáte nyní pěkný breakout, řádek vrácený pro každou jednotku zakoupeného produktu, objednanou podle data nákupu. Všimněte si, že také třídíme podle PurchaseID, abychom zvládli případ, kdy došlo k vícenásobným nákupům stejného produktu ve stejný den, takže musíme nějak zlomit vazbu, abychom zajistili konzistentní výpočet údajů za cenu. Poté můžeme aktualizovat dočasnou tabulku nákupem:
WITH PurchaseData AS (
<previous query>
)
MERGE INTO dbo.tblProductTransactionStaging AS t
USING PurchaseData AS p
ON t.ProductID = p.ProductID
AND t.TransactionNumber = p.TransactionNumber
WHEN MATCHED THEN UPDATE SET
t.PurchaseID = p.PurchaseID,
t.PurchaseDate = p.PurchaseDate,
t.CostPer = p.CostPer;

Část objednávek je v podstatě to samé – jednoduše nahraďte „Koupit“ za „Objednávka“ a tabulka se zaplní stejně jako v původním obrázku na začátku příspěvku.

A v tomto okamžiku jste všichni připraveni dělat všechny ostatní účetní dobroty, když jste produkty rozdělili z úrovně transakce dolů na úroveň jednotky, kterou potřebujete k přesnému mapování nákladů na zboží k výnosům. pro tuto konkrétní jednotku produktu pomocí FIFO nebo LIFO podle požadavků vašeho účetního. Výpočty jsou nyní základní.

Granularita ve světě OLTP

Koncept granularity je koncept častější v datových skladech než v aplikacích OLTP, ale myslím si, že diskutovaný scénář zdůrazňuje potřebu ustoupit a jasně určit, jaká je současná granularita schématu OLTP. Jak jsme viděli, na začátku jsme měli špatnou granularitu a potřebovali jsme přepracovat, abychom mohli získat granularitu potřebnou k dosažení našich přehledů. Byla to šťastná náhoda, že v tomto případě můžeme přesně snížit granularitu, protože již máme k dispozici všechna data komponent, takže jsme jednoduše museli data transformovat. To není vždy případ a je pravděpodobnější, že pokud schéma není dostatečně podrobné, bude vyžadovat přepracování schématu. Nicméně identifikace granularity potřebné ke splnění požadavků pomáhá jasně definovat logické kroky, které musíte podniknout, abyste dosáhli tohoto cíle.

Dokončete skript SQL, který demonstruje bod, který lze získat DemoLowGranularity.sql.


  1. Znamená vývoj kontaktních informací změnu vaší databáze?

  2. Funkce COMPOSE() v Oracle

  3. SQLiteException:neznámá databáze

  4. Jak správně zavřít kurzor v Androidu