sql >> Database teknologi >  >> RDS >> Database

Kunsten at aggregere data i SQL fra simple til glidende aggregationer

Lad os starte vores SQL-rejse for at forstå aggregeringsdata i SQL og typer af aggregeringer, herunder simple og glidende aggregeringer.

Før vi hopper til aggregeringerne, er det værd at overveje interessante fakta, som nogle udviklere ofte savner, når det kommer til SQL generelt og aggregeringen i særdeleshed.

I denne artikel refererer SQL til T-SQL, som er Microsoft-versionen af ​​SQL og har flere funktioner end standard SQL.

Matematik bag SQL

Det er meget vigtigt at forstå, at T-SQL er baseret på nogle solide matematiske begreber, selvom det ikke er et stift matematikbaseret sprog.

Ifølge bogen "Microsoft_SQL_Server_2008_T_SQL_Fundamentals" af Itzik Ben-Gan er SQL designet til at forespørge og administrere data i et relationelt databasestyringssystem (RDBMS).

Selve det relationelle databasestyringssystem er baseret på to solide matematiske grene:

  • Mængdeteori
  • Prdikatlogik

Mængdeteori

Mængdeori, som navnet indikerer, er en gren af ​​matematikken om mængder, som også kan kaldes samlinger af bestemte distinkte objekter.

Kort sagt, i mængdeteori tænker vi på ting eller objekter som en helhed på samme måde, som vi tænker på en individuel genstand.

For eksempel er en bog et sæt af alle de bestemte særskilte bøger, så vi tager en bog som en helhed, hvilket er nok til at få detaljer om alle bøgerne i den.

Prdikatlogik

Prædikatlogik er en boolsk logik, som returnerer sand eller falsk afhængigt af variablernes betingelse eller værdier.

Prædikatlogik kan bruges til at håndhæve integritetsregler (prisen skal være større end 0,00) eller filtrere data (hvor prisen er mere end 10,00), men i forbindelse med T-SQL har vi tre logiske værdier som følger:

  1. Sandt
  2. Falsk
  3. Ukendt (Nul)

Dette kan illustreres som følger:

Et eksempel på et prædikat er "Hvor prisen på bogen er større end 10,00".

Det er nok om matematik, men husk, at jeg vil henvise til det senere i artiklen.

Hvorfor er det nemt at aggregere data i SQL

At aggregere data i SQL i sin enkleste form handler om at få at vide om totalerne på én gang.

For eksempel, hvis vi har en kundetabel, som indeholder en liste over alle kunderne sammen med deres detaljer, kan aggregerede data fra kundetabellen give os det samlede antal kunder, vi har.

Som diskuteret tidligere, tænker vi på et sæt som et enkelt element, så vi anvender simpelthen en aggregeret funktion på tabellen for at få totalerne.

Da SQL oprindeligt er et sætbaseret sprog (som diskuteret tidligere), så er det relativt nemmere at anvende aggregerede funktioner på det sammenlignet med andre sprog.

For eksempel, hvis vi har en produkttabel, som har registreringer af alle produkterne i databasen, kan vi med det samme anvende tællefunktionen på en produkttabel for at få det samlede antal produkter i stedet for at tælle dem en efter en i en løkke.

Dataaggregationsopskrift

For at aggregere data i SQL har vi som minimum brug for følgende ting:

  1. Data (tabel) med kolonner, der giver mening, når de er aggregerede
  2. En samlet funktion, der skal anvendes på dataene

Forberedelse af prøvedata (tabel)

Lad os tage et eksempel på en simpel rækkefølgetabel, som indeholder tre ting (kolonner):

  1. Ordrenummer (OrderId)
  2. Dato, hvor ordren blev afgivet (OrderDate)
  3. Beløbet for ordren (TotalAmount)

Lad os oprette AggregateSample-databasen for at komme videre:

-- Create aggregate sample database 
CREATE DATABASE AggregateSample

Opret nu ordretabellen i eksempeldatabasen som følger:

-- Create order table in the aggregate sample database
USE AggregateSample

CREATE TABLE SimpleOrder
  (OrderId INT PRIMARY KEY IDENTITY(1,1),
  OrderDate DATETIME2,
  TotalAmount DECIMAL(10,2)
  )

Populering af prøvedata

Udfyld tabellen ved at tilføje en række:

INSERT INTO dbo.SimpleOrder
(
  OrderDate
 ,TotalAmount
)
VALUES
(
  '20180101' -- OrderDate - datetime2
 ,20.50 -- TotalAmount - decimal(10, 2)
);
GO

Lad os se på tabellen nu:

-- View order table 
SELECT OrderId ,OrderDate ,TotalAmount FROM SimpleOrder

Bemærk venligst, at jeg bruger dbForge Studio til SQL Server i denne artikel, så kun output-looket kan variere, hvis du kører den samme kode i SSMS (SQL Server Management Studio), der er ingen forskel, hvad angår scripts og deres resultater.

Grundlæggende aggregerede funktioner

De grundlæggende aggregerede funktioner, der kan anvendes på tabellen, er som følger:

  1. Sum
  2. Tæl
  3. Min.
  4. Maks.
  5. Gennemsnit

Aggregerende enkeltoptagelsestabel

Nu er det interessante spørgsmål, "kan vi aggregere (sum eller tælle) data (registreringer) i en tabel, hvis den kun har én række som i vores tilfælde?" Svaret er "Ja", det kan vi, selvom det ikke giver meget mening, men det kan hjælpe os med at forstå, hvordan data bliver klar til aggregering.

For at få det samlede antal ordrer, bruger vi funktionen count() med tabellen, som diskuteret tidligere, vi kan simpelthen anvende aggregatfunktionen på tabellen, da SQL er et sæt baseret sprog og operationer kan anvendes på et sæt direkte.

-- Getting total number of orders placed so far
SELECT COUNT(*) AS Total_Orders FROM SimpleOrder

Hvad nu med ordren med et minimum, maksimum og gennemsnitsbeløb for en enkelt post:

-- Getting order with minimum amount, maximum amount, average amount and total orders
SELECT
  COUNT(*) AS Total_Orders
 ,MIN(TotalAmount) AS Min_Amount
 ,MAX(TotalAmount) AS Max_Amount
 ,AVG(TotalAmount) Average_Amount
FROM SimpleOrder

Som vi kan se fra outputtet, er minimum-, maksimum- og gennemsnitsbeløbet det samme, hvis vi har en enkelt post, så det er muligt at anvende en aggregeret funktion på en enkelt post, men det giver os de samme resultater.

Vi har brug for mindst mere end én registrering for at give mening i de aggregerede data.

Tabel med sammenlægning af flere poster

Lad os nu tilføje yderligere fire poster som følger:

INSERT INTO dbo.SimpleOrder
(
  OrderDate
 ,TotalAmount
)
VALUES
(
  '20180101' -- OrderDate - datetime2
 ,20.50 -- TotalAmount - decimal(10, 2)
),
(
  '20180102' -- OrderDate - datetime2
 ,30.50 -- TotalAmount - decimal(10, 2)
),
(
  '20180103' -- OrderDate - datetime2
 ,10.50 -- TotalAmount - decimal(10, 2)
),
(
  '20180110' -- OrderDate - datetime2
 ,100.50 -- TotalAmount - decimal(10, 2)
);

GO

Tabellen ser nu ud som følger:

Hvis vi anvender de samlede funktioner på tabellen nu, vil vi få gode resultater:

-- Getting order with minimum amount, maximum amount, average amount and total orders
SELECT
  COUNT(*) AS Total_Orders
 ,MIN(TotalAmount) AS Min_Amount
 ,MAX(TotalAmount) AS Max_Amount
 ,AVG(TotalAmount) Average_Amount
FROM SimpleOrder

Grupper aggregerede data

Vi kan gruppere de aggregerede data efter en hvilken som helst kolonne eller et sæt kolonner for at få aggregater baseret på den kolonne.

For eksempel, hvis vi ønsker at vide det samlede antal ordrer pr. dato, vi skal gruppere tabellen efter dato ved at bruge Group by-sætning som følger:

-- Getting total orders per date
SELECT
  OrderDate
 ,COUNT(*) AS Total_Orders
FROM SimpleOrder
GROUP BY OrderDate

Udgangen er som følger:

Så hvis vi ønsker at se summen af ​​hele ordrebeløbet, vi kan ganske enkelt anvende sumfunktionen på kolonnen samlet beløb uden nogen gruppering som følger:

-- Sum of all the orders amount
SELECT
  SUM(TotalAmount) AS Sum_of_Orders_Amount
FROM SimpleOrder

For at få summen af ​​ordrebeløb pr. dato tilføjer vi blot gruppe efter dato til ovenstående SQL-sætning som følger:

-- Sum of	all	the	orders amount per date
SELECT
  OrderDate
 ,SUM(TotalAmount) AS Sum_of_Orders
FROM SimpleOrder
GROUP BY OrderDate

Få totaler uden at gruppere data

Vi kan med det samme få totaler såsom samlede ordrer, maksimum ordrebeløb, minimum ordrebeløb, sum af ordrebeløb, gennemsnitlig ordrebeløb uden behov for at gruppere det, hvis aggregeringen er beregnet til alle tabellerne.

-- Getting order with minimum amount, maximum amount, average amount, sum of amount and total orders
SELECT
  COUNT(*) AS Total_Orders
 ,MIN(TotalAmount) AS Min_Amount
 ,MAX(TotalAmount) AS Max_Amount
 ,AVG(TotalAmount) AS Average_Amount
 ,SUM(TotalAmount) AS Sum_of_Amount
FROM SimpleOrder

Tilføjelse af kunder til ordrerne

Lad os tilføje noget sjov ved at tilføje kunder i vores tabel. Vi kan gøre dette ved at oprette en anden tabel over kunder og videregive kunde-id til ordretabellen, men for at holde det simpelt og for at håne datavarehus-stilen (hvor tabeller er denormaliserede), tilføjer jeg kolonnen kundenavn i ordretabellen som følger :

-- Adding CustomerName column and data to the order table
ALTER TABLE SimpleOrder 
ADD CustomerName VARCHAR(40) NULL 
  GO
  
UPDATE SimpleOrder
SET CustomerName = 'Eric'
WHERE OrderId = 1
GO

UPDATE SimpleOrder
SET CustomerName = 'Sadaf'
WHERE OrderId = 2
GO

UPDATE SimpleOrder
SET CustomerName = 'Peter'
WHERE OrderId = 3
GO

UPDATE SimpleOrder
SET CustomerName = 'Asif'
WHERE OrderId = 4
GO

UPDATE SimpleOrder
SET CustomerName = 'Peter'
WHERE OrderId = 5
GO

Få samlede ordrer pr. kunde

Kan du nu gætte, hvordan man får samlede ordrer pr. kunde? Du skal gruppere efter kunde (CustomerName) og anvende den samlede funktion count() på alle posterne som følger:

-- Total orders per customer
  SELECT CustomerName,COUNT(*) AS Total_Orders FROM SimpleOrder 
    GROUP BY CustomerName

Tilføjelse af yderligere fem poster til ordretabellen

Nu skal vi tilføje fem rækker mere til den simple rækkefølge tabel som følger:

-- Adding 5 more records to order table
INSERT INTO SimpleOrder (OrderDate, TotalAmount, CustomerName)
  VALUES 
  ('01-Jan-2018', 70.50, 'Sam'),
  ('02-Jan-2018', 170.50, 'Adil'),
  ('03-Jan-2018',50.00,'Sarah'),
  ('04-Jan-2018',50.00,'Asif'),
  ('11-Jan-2018',50.00,'Peter')
GO

Tag et kig på dataene nu:

-- Viewing order table after adding customer name and five more rows
SELECT OrderId,CustomerName,OrderDate,TotalAmount FROM SimpleOrder 
GO

Få samlede ordrer pr. kunde sorteret efter maksimum til minimum ordrer

Hvis du er interesseret i de samlede ordrer pr. kunde sorteret efter maksimum til minimum ordrer, er det slet ikke en dårlig idé at dele dette op i mindre trin som følger:

-- (1) Getting total orders
SELECT COUNT(*) AS Total_Orders FROM SimpleOrder

-- (2) Getting total orders per customer
SELECT CustomerName,COUNT(*) AS Total_Orders FROM SimpleOrder
GROUP BY CustomerName

For at sortere ordrer tæller fra maksimum til minimum, skal vi bruge Ordre efter DESC (faldende rækkefølge) klausulen med count() i slutningen som følger:

-- (3) Getting total orders per customer from maximum to minimum orders
SELECT CustomerName,COUNT(*) AS Total_Orders FROM SimpleOrder
GROUP BY CustomerName
ORDER BY COUNT(*) DESC

Få det samlede antal ordrer pr. dato sorteret efter seneste ordre først

Ved at bruge ovenstående metode kan vi nu finde ud af de samlede ordrer pr. dato sorteret efter seneste ordre først som følger:

-- Getting total orders per date from most recent first
SELECT CAST(OrderDate AS DATE) AS OrderDate,COUNT(*) AS Total_Orders FROM SimpleOrder
GROUP BY OrderDate
ORDER BY OrderDate DESC

CAST-funktionen hjælper os med kun at få datodelen. Outputtet er som følger:

Du kan bruge så mange kombinationer som muligt, så længe de giver mening.

Kørende aggregationer

Nu hvor vi er fortrolige med at anvende aggregerede funktioner på vores data, lad os gå videre til den avancerede form for aggregering, og en sådan aggregering er den løbende aggregering.

Kørende aggregeringer er de aggregeringer, der anvendes på en delmængde af data i stedet for på hele datasættet, hvilket hjælper os med at skabe små vinduer på dataene.

Hidtil har vi set alle aggregerede funktioner anvendes på alle rækker i tabellen, som kan grupperes efter en kolonne som ordredato eller kundenavn, men med kørende aggregeringer har vi frihed til at anvende aggregerede funktioner uden at gruppere hele datasæt.

Det betyder naturligvis, at vi kan anvende aggregatfunktionen uden at bruge Group By-klausulen, hvilket er noget mærkeligt for de SQL-begyndere (eller nogle gange overser nogle udviklere dette), som ikke er fortrolige med vinduesfunktionerne og kørende aggregeringer.

Windows på data

Som tidligere nævnt anvendes den kørende aggregering på et undersæt af datasæt eller (med andre ord) på små vinduer med data.

Tænk på vinduer som et sæt i et sæt eller en tabel(r) i en tabel. Et godt eksempel på vinduer på data i vores tilfælde er, at vi har ordretabellen, som indeholder ordrer afgivet på forskellige datoer, så hvad hvis hver dato er et separat vindue, så kan vi anvende aggregerede funktioner på hvert vindue på samme måde, som vi anvendte til bordet.

Hvis vi sorterer ordretabellen (SimpleOrder) efter ordredato (OrderDate) som følger:

-- View order table sorted by order date
SELECT so.OrderId
      ,so.OrderDate
      ,so.TotalAmount
      ,so.CustomerName FROM SimpleOrder so
  ORDER BY so.OrderDate

Windows på data klar til at køre sammenlægninger kan ses nedenfor:

Vi kan også betragte disse vinduer eller undersæt som seks miniordre datobaserede tabeller og aggregater kan anvendes på hver af disse minitabeller.

Brug af partition af inde i OVER() klausul

Kørende aggregeringer kan anvendes ved at partitionere tabellen ved hjælp af "Partition by" inde i OVER()-sætningen.

For eksempel, hvis vi ønsker at opdele rækkefølgetabellen efter datoer, såsom at hver dato er en undertabel eller et vindue på datasættet, så skal vi opdele data efter ordredato, og dette kan opnås ved at bruge en aggregeret funktion såsom COUNT( ) med OVER() og partitioner med inde i OVER() som følger:

-- Running Aggregation on Order table by partitioning by dates
SELECT OrderDate, Total_Orders=COUNT(*) OVER(PARTITION BY OrderDate)  FROM SimpleOrder

Hent løbende totaler pr. dato vindue (partition)

Kørende aggregeringer hjælper os med at begrænse aggregeringsomfanget til kun det definerede vindue, og vi kan få løbende totaler pr. vindue som følger:

-- Getting total orders, minimum amount, maximum amount, average amount and sum of all amounts per date window (partition by date)
SELECT CAST (OrderDate AS DATE) AS OrderDate,
  Count=COUNT(*) OVER (PARTITION BY OrderDate),
  Min_Amount=MIN(TotalAmount) OVER (PARTITION BY OrderDate) ,
  Max_Amount=MAX(TotalAmount) OVER (PARTITION BY OrderDate) ,
  Average_Amount=AVG(TotalAmount) OVER (PARTITION BY OrderDate),
  Sum_Amount=SUM(TotalAmount) OVER (PARTITION BY OrderDate)
  FROM SimpleOrder

Få løbende totaler pr. kundevindue (partition)

Ligesom de løbende totaler pr. datovindue, kan vi også beregne løbende totaler pr. kundevindue ved at opdele ordresættet (tabellen) i små kundeundersæt (partitioner) som følger:

-- Getting total orders, minimum amount, maximum amount, average amount and sum of all amounts per customer window (partition by customer)
SELECT CustomerName,
CAST (OrderDate AS DATE) AS OrderDate,
  Count=COUNT(*) OVER (PARTITION BY CustomerName),
  Min_Amount=MIN(TotalAmount) OVER (PARTITION BY CustomerName) ,
  Max_Amount=MAX(TotalAmount) OVER (PARTITION BY CustomerName) ,
  Average_Amount=AVG(TotalAmount) OVER (PARTITION BY CustomerName),
  Sum_Amount=SUM(TotalAmount) OVER (PARTITION BY CustomerName)
  FROM SimpleOrder
  ORDER BY Count DESC,OrderDate

Glidende aggregationer

Glidende sammenlægninger er de sammenlægninger, der kan anvendes på rammerne i et vindue, hvilket betyder, at man indsnævrer omfanget yderligere inden for vinduet (partition).

Med andre ord giver løbende totaler os totaler (sum, gennemsnit, min, maks, antal) for hele vinduet (delmængde), vi opretter i en tabel, mens glidende totaler giver os totaler (sum, gennemsnit, min, maks, tæller) for rammen (undermængde af undermængde) inden for vinduet (undermængde) i tabellen.

For eksempel, hvis vi opretter et vindue på data baseret på (opdeling efter kunde) kunde, så kan vi se, at kunden "Peter" har tre poster i sit vindue, og alle sammenlægninger anvendes på disse tre poster. Hvis vi nu kun vil oprette en ramme for to rækker ad gangen, betyder det, at aggregeringen bliver yderligere indsnævret, og at den derefter anvendes på den første og anden række og derefter den anden og tredje række og så videre.

Brug af RÆKKER FOREGÅENDE med Order By inde i OVER()-klausul

Glidende aggregeringer kan anvendes ved at tilføje RÆKKER PRECEEDING med rækkefølge efter (efter partition efter), mens RÆKKER PRECEEDING bestemmer omfanget af Frame i vinduet.

Hvis vi f.eks. kun ønsker at aggregere data for to rækker ad gangen for hver kunde, skal vi bruge glidende aggregeringer til ordretabellen som følger:

-- Getting minimum amount, maximum amount, average amount per frame per customer window 
SELECT CustomerName,
 Min_Amount=Min(TotalAmount) OVER (PARTITION BY CustomerName ORDER BY OrderDate ROWS 1 PRECEDING), 
 Max_Amount=Max(TotalAmount) OVER (PARTITION BY CustomerName ORDER BY OrderDate ROWS 1 PRECEDING) ,
 Average_Amount=AVG(TotalAmount) OVER (PARTITION BY CustomerName ORDER BY OrderDate  ROWS 1 PRECEDING)
 FROM SimpleOrder so
 ORDER BY CustomerName

For at forstå, hvordan det virker, lad os se på den originale tabel i sammenhæng med rammer og vinduer:

I den første række af kunden Peter vinduet afgav han en ordre med beløbet 30,50, da dette er begyndelsen af ​​rammen inden for kundevinduet, så min og max er det samme, da der ikke er nogen tidligere række at sammenligne med.

Dernæst forbliver minimumsbeløbet det samme, men maksimumbeløbet bliver 100,50, da beløbet i den foregående række (første række) er 30,50, og dette rækkebeløb er 100,50, så maksimum af de to er 100,50.

Når du derefter flytter til den tredje række, vil sammenligningen finde sted med den anden række, så minimumsbeløbet af de to er 50,00 og det maksimale antal af de to rækker er 100,50.

MDX Year to Date (YTD) funktion og løbende aggregationer

MDX er et flerdimensionelt udtrykssprog, der bruges til at forespørge multidimensionelle data (såsom kube) og bruges i business intelligence (BI)-løsninger.

Ifølge https://docs.microsoft.com/en-us/sql/mdx/ytd-mdx fungerer funktionen Year to Date (YTD) i MDX på samme måde som kørende eller glidende aggregeringer. For eksempel viser YTD ofte brugt i kombination med ingen angivet parameter en løbende total til dato.

Det betyder, at hvis vi anvender denne funktion på år, giver den alle årsdata, men hvis vi borer ned til marts, vil det give os alle totalerne fra begyndelsen af ​​året til marts og så videre.

Dette er meget nyttigt i SSRS-rapporter.

Ting at gøre

Det er det! Du er klar til at lave nogle grundlæggende dataanalyse efter at have gennemgået denne artikel, og du kan forbedre dine færdigheder yderligere ved at gøre følgende:

  1. Prøv venligst at skrive et kørende aggregeringsscript ved at oprette vinduer på andre kolonner, f.eks. Samlet beløb.
  2. Prøv også at skrive et glidende aggregeret script ved at oprette rammer på andre kolonner, f.eks. Samlet beløb.
  3. Du kan tilføje flere kolonner og poster til tabellen (eller endnu flere tabeller) for at prøve andre aggregeringskombinationer.
  4. De eksempler på scripts, der er nævnt i denne artikel, kan omdannes til lagrede procedurer, der skal bruges i SSRS-rapporter bag datasæt(er).

Referencer:

  • Ytd (MDX)
  • dbForge Studio til SQL Server

  1. Kan ikke installere pg gem på Windows

  2. GROUP BY / aggregeret funktionsforvirring i SQL

  3. MySQL ACOS() Funktion – Returner Arc Cosinus for et tal

  4. Sådan viser du alle Oracle-databaseprivilegier for en bruger