Database
 sql >> Teknologi Basis Data >  >> RDS >> Database

Seni Menggabungkan Data dalam SQL dari Sederhana ke Agregasi Geser

Mari kita mulai perjalanan SQL kita untuk memahami agregasi data dalam SQL dan jenis agregasi termasuk agregasi sederhana dan geser.

Sebelum kita beralih ke agregasi, ada baiknya mempertimbangkan fakta menarik yang sering terlewatkan oleh beberapa pengembang ketika datang ke SQL pada umumnya dan agregasi pada khususnya.

Dalam artikel ini, SQL mengacu pada T-SQL yang merupakan versi Microsoft dari SQL dan memiliki lebih banyak fitur daripada SQL standar.

Matematika di balik SQL

Sangat penting untuk memahami bahwa T-SQL didasarkan pada beberapa konsep matematika yang solid meskipun bukan bahasa berbasis matematika yang kaku.

Menurut buku “Microsoft_SQL_Server_2008_T_SQL_Fundamentals” oleh Itzik Ben-Gan, SQL dirancang untuk membuat kueri dan mengelola data dalam sistem manajemen basis data relasional (RDBMS).

Sistem manajemen basis data relasional itu sendiri didasarkan pada dua cabang matematika yang solid:

  • Teori Himpunan
  • Logika Predikat

Teori Himpunan

Teori himpunan, seperti namanya, adalah cabang matematika tentang himpunan yang juga bisa disebut kumpulan objek-objek tertentu yang berbeda.

Singkatnya, dalam teori himpunan, kita memikirkan hal-hal atau objek secara keseluruhan dengan cara yang sama seperti kita memikirkan item individual.

Misalnya, sebuah buku adalah kumpulan dari semua buku tertentu yang berbeda, jadi, kami mengambil sebuah buku secara keseluruhan yang cukup untuk mendapatkan detail semua buku di dalamnya.

Logika Predikat

Logika predikat adalah logika Boolean yang mengembalikan nilai true atau false tergantung pada kondisi atau nilai variabel.

Logika predikat dapat digunakan untuk menegakkan aturan integritas (harga harus lebih besar dari 0,00) atau menyaring data (di mana harga lebih dari 10,00), namun, dalam konteks T-SQL, kami memiliki tiga nilai logika sebagai berikut:

  1. Benar
  2. Salah
  3. Tidak Diketahui (Null)

Hal ini dapat digambarkan sebagai berikut:

Contoh predikat adalah “Di mana Harga Buku lebih besar dari 10.00”.

Itu sudah cukup tentang matematika, tetapi harap diingat bahwa saya akan merujuknya nanti di artikel.

Mengapa Menggabungkan Data dalam SQL itu Mudah

Menggabungkan data dalam SQL dalam bentuk yang paling sederhana adalah tentang mengetahui total dalam sekali jalan.

Misalnya, jika kita memiliki tabel pelanggan yang berisi daftar semua pelanggan beserta detailnya, maka kumpulan data tabel pelanggan dapat memberi kita jumlah total pelanggan yang telah kita dapatkan.

Seperti yang telah dibahas sebelumnya, kita menganggap himpunan sebagai item tunggal sehingga kita cukup menerapkan fungsi agregat ke tabel untuk mendapatkan totalnya.

Karena SQL pada awalnya adalah bahasa berbasis kumpulan (seperti yang dibahas sebelumnya), maka relatif lebih mudah untuk menerapkan fungsi agregat dibandingkan dengan bahasa lain.

Misalnya, jika kita memiliki tabel produk yang memiliki catatan semua produk dalam database, maka kita dapat langsung menerapkan fungsi hitung ke tabel produk untuk mendapatkan jumlah total produk daripada menghitungnya satu per satu dalam satu lingkaran.

Resep Agregasi Data

Untuk menggabungkan data dalam SQL, minimal kita memerlukan hal-hal berikut:

  1. Data (tabel) dengan kolom yang jika digabungkan masuk akal
  2. Fungsi agregat yang akan diterapkan pada data

Menyiapkan Data Sampel (Tabel)

Mari kita ambil contoh tabel pesanan sederhana yang berisi tiga hal (kolom):

  1. Nomor pesanan (IdPesanan)
  2. Tanggal saat pemesanan dilakukan (OrderDate)
  3. Jumlah pesanan (TotalJumlah)

Mari kita buat database AggregateSample untuk melangkah lebih jauh:

-- Create aggregate sample database 
CREATE DATABASE AggregateSample

Sekarang buat tabel pesanan di database sampel sebagai berikut:

-- 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)
  )

Mengisi Data Sampel

Isi tabel dengan menambahkan satu baris:

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

Mari kita lihat tabelnya sekarang:

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

Harap dicatat bahwa saya menggunakan dbForge Studio untuk SQL Server dalam artikel ini jadi hanya tampilan keluaran yang mungkin berbeda jika Anda menjalankan kode yang sama di SSMS (SQL Server Management Studio), tidak ada perbedaan sejauh menyangkut skrip dan hasilnya.

Fungsi Agregat Dasar

Fungsi agregat dasar yang dapat diterapkan pada tabel adalah sebagai berikut:

  1. Jumlah
  2. Hitung
  3. Min
  4. Maks
  5. Rata-rata

Menggabungkan Tabel Rekaman Tunggal

Sekarang pertanyaan yang menarik adalah, “dapatkah kita menggabungkan (menjumlahkan atau menghitung) data (rekaman) dalam sebuah tabel jika hanya memiliki satu baris seperti dalam kasus kita?” Jawabannya adalah “Ya”, kita bisa, meskipun tidak masuk akal tetapi dapat membantu kita untuk memahami bagaimana data siap untuk agregasi.

Untuk mendapatkan jumlah total pesanan, kita menggunakan fungsi count() dengan tabel, seperti yang dibahas sebelumnya, kita cukup menerapkan fungsi agregat ke tabel karena SQL adalah bahasa berbasis himpunan dan operasi dapat diterapkan ke himpunan. secara langsung.

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

Sekarang, bagaimana dengan pesanan dengan jumlah minimum, maksimum dan rata-rata untuk satu record:

-- 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

Seperti yang dapat kita lihat dari output, jumlah minimum, maksimum, dan rata-rata adalah sama jika kita memiliki satu record, jadi menerapkan fungsi agregat ke satu record dimungkinkan tetapi memberikan hasil yang sama.

Kami membutuhkan setidaknya lebih dari satu catatan untuk memahami data yang dikumpulkan.

Menggabungkan Beberapa Tabel Catatan

Sekarang mari kita tambahkan empat record lagi sebagai berikut:

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

Tabel sekarang terlihat sebagai berikut:

Jika kita menerapkan fungsi agregat ke tabel sekarang, kita akan mendapatkan hasil yang baik:

-- 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

Mengelompokkan Data Gabungan

Kami dapat mengelompokkan data agregat menurut kolom atau kumpulan kolom mana pun untuk mendapatkan agregat berdasarkan kolom tersebut.

Misalnya, jika kita ingin mengetahui jumlah total pesanan per tanggal, kita harus mengelompokkan tabel berdasarkan tanggal menggunakan klausa Group by sebagai berikut:

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

Outputnya adalah sebagai berikut:

Jadi jika kita ingin melihat jumlah semua jumlah pesanan, kita cukup menerapkan fungsi jumlah ke kolom jumlah total tanpa pengelompokan sebagai berikut:

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

Untuk mendapatkan jumlah jumlah pesanan per tanggal, kita cukup menambahkan grup menurut tanggal ke pernyataan SQL di atas sebagai berikut:

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

Mendapatkan Total tanpa Mengelompokkan Data

Kita bisa langsung mendapatkan total seperti total pesanan, jumlah pesanan maksimum, jumlah pesanan minimum, jumlah jumlah pesanan, jumlah pesanan rata-rata tanpa perlu mengelompokkannya jika agregasi dimaksudkan untuk semua tabel.

-- 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

Menambahkan Pelanggan ke Pesanan

Mari kita tambahkan kesenangan dengan menambahkan pelanggan di meja kita. Kita dapat melakukan ini dengan membuat tabel pelanggan lain dan meneruskan id pelanggan ke tabel pesanan, namun untuk membuatnya tetap sederhana dan untuk meniru gaya gudang data (di mana tabel didenormalisasi), saya menambahkan kolom nama pelanggan di tabel pesanan sebagai berikut :

-- 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

Mendapatkan Total Pesanan per Pelanggan

Bisakah Anda menebak sekarang bagaimana mendapatkan total pesanan per pelanggan? Anda perlu mengelompokkan menurut pelanggan (NamaPelanggan) dan menerapkan fungsi agregat count() ke semua catatan sebagai berikut:

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

Menambahkan Lima Catatan Lagi ke Tabel Pesanan

Sekarang kita akan menambahkan lima baris lagi ke tabel pesanan sederhana sebagai berikut:

-- 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

Lihat datanya sekarang:

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

Mendapatkan Total Pesanan per pelanggan yang diurutkan berdasarkan pesanan maksimum hingga minimum

Jika Anda tertarik dengan total pesanan per pelanggan yang diurutkan berdasarkan pesanan maksimum hingga minimum, bukanlah ide yang buruk untuk membaginya menjadi langkah-langkah yang lebih kecil sebagai berikut:

-- (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

Untuk mengurutkan jumlah pesanan dari maksimum ke minimum, kita perlu menggunakan klausa Order By DESC (descending order) dengan count() di akhir sebagai berikut:

-- (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

Mendapatkan Total Pesanan per tanggal diurutkan berdasarkan pesanan terbaru terlebih dahulu

Dengan menggunakan metode di atas, sekarang kita dapat mengetahui total pesanan per tanggal yang diurutkan berdasarkan pesanan terbaru terlebih dahulu sebagai berikut:

-- 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

Fungsi CAST membantu kita untuk mendapatkan bagian tanggal saja. Outputnya adalah sebagai berikut:

Anda dapat menggunakan kombinasi sebanyak mungkin selama masih masuk akal.

Menjalankan Agregasi

Sekarang setelah kita terbiasa menerapkan fungsi agregat ke data kita, mari kita beralih ke bentuk agregasi lanjutan dan salah satu agregasi tersebut adalah agregasi berjalan.

Menjalankan agregasi adalah agregasi yang diterapkan ke subset data dan bukan ke seluruh dataset yang membantu kita membuat jendela kecil pada data.

Sejauh ini kita telah melihat semua fungsi agregat diterapkan ke semua baris tabel yang dapat dikelompokkan berdasarkan beberapa kolom seperti tanggal pemesanan atau nama pelanggan, tetapi dengan menjalankan agregasi kita memiliki kebebasan untuk menerapkan fungsi agregat tanpa mengelompokkan keseluruhan kumpulan data.

Jelas, ini berarti kita dapat menerapkan fungsi agregat tanpa menggunakan klausa Group By yang agak aneh bagi pemula SQL (atau terkadang beberapa pengembang mengabaikannya) yang tidak terbiasa dengan fungsi windowing dan menjalankan agregasi.

Windows pada Data

Seperti yang dikatakan sebelumnya, agregasi berjalan diterapkan ke subset dataset atau (dengan kata lain) pada jendela data kecil.

Pikirkan windows sebagai set di dalam set atau tabel di dalam tabel. Contoh yang baik dari windowing pada data dalam kasus kami adalah, kami memiliki tabel pesanan yang berisi pesanan yang ditempatkan pada tanggal yang berbeda, jadi bagaimana jika setiap tanggal adalah jendela yang terpisah, maka kami dapat menerapkan fungsi agregat pada setiap jendela dengan cara yang sama seperti yang kami terapkan. meja.

Jika kita mengurutkan tabel pemesanan (SimpleOrder) berdasarkan tanggal pemesanan (OrderDate) sebagai berikut:

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

Windows pada data yang siap menjalankan agregasi dapat dilihat di bawah ini:

Kami juga dapat mempertimbangkan jendela atau himpunan bagian ini sebagai enam tabel berbasis tanggal pesanan mini dan agregat dapat diterapkan pada masing-masing tabel mini ini.

Penggunaan Partisi Oleh di dalam Klausa OVER()

Menjalankan agregasi dapat diterapkan dengan mempartisi tabel menggunakan “Partition by” di dalam klausa OVER().

Misalnya, jika kita ingin mempartisi tabel pesanan berdasarkan tanggal seperti setiap tanggal adalah sub-tabel atau jendela pada dataset, maka kita harus mempartisi data berdasarkan tanggal pesanan dan ini dapat dicapai dengan menggunakan fungsi agregat seperti COUNT( ) dengan OVER() dan Partition by inside OVER() sebagai berikut:

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

Mendapatkan Total Berjalan per jendela Tanggal (Partisi)

Menjalankan agregasi membantu kami membatasi cakupan agregasi ke jendela yang ditentukan saja dan kami bisa mendapatkan total berjalan per jendela sebagai berikut:

-- 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

Mendapatkan Total Berjalan per Jendela Pelanggan (Partisi)

Sama seperti total berjalan per jendela tanggal, kami juga dapat menghitung total berjalan per jendela pelanggan dengan mempartisi set pesanan (tabel) menjadi subset pelanggan kecil (partisi) sebagai berikut:

-- 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

Agregasi Geser

Agregasi geser adalah agregasi yang dapat diterapkan ke bingkai di dalam jendela yang berarti mempersempit cakupan lebih jauh di dalam jendela (partisi).

Dengan kata lain, total berjalan memberi kita total (jumlah, rata-rata, min, maks, hitung) untuk seluruh jendela (subset) yang kita buat dalam tabel, sementara total geser memberi kita total (jumlah, rata-rata, min, maks, hitung) untuk frame (subset dari subset) di dalam jendela (subset) tabel.

Misalnya, jika kita membuat jendela pada data berdasarkan (dipartisi oleh pelanggan) pelanggan maka kita dapat melihat pelanggan "Peter" memiliki tiga catatan di jendelanya dan semua agregasi diterapkan ke tiga catatan ini. Sekarang jika kita ingin membuat bingkai untuk dua baris hanya pada satu waktu itu berarti agregasi lebih dipersempit dan kemudian diterapkan pada baris pertama dan kedua dan kemudian baris kedua dan ketiga dan seterusnya.

Penggunaan ROWS PRECEEDING dengan Order By di dalam OVER() Clause

Agregasi geser dapat diterapkan dengan menambahkan ROWS PRECEEDING dengan Order By (setelah Partition By) sedangkan ROWS PRECEEDING menentukan cakupan Frame dalam jendela.

Misalnya, jika kita ingin menggabungkan data hanya untuk dua baris sekaligus untuk setiap pelanggan, maka kita perlu agregasi geser untuk diterapkan ke tabel pesanan sebagai berikut:

-- 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

Untuk memahami cara kerjanya, mari kita lihat tabel asli dalam konteks bingkai dan jendela:

Di baris pertama jendela pelanggan Peter, ia menempatkan pesanan dengan jumlah 30,50 karena ini adalah awal dari bingkai dalam jendela pelanggan, jadi min dan maks sama dengan tidak ada baris sebelumnya untuk dibandingkan.

Selanjutnya jumlah minimum tetap sama tetapi maksimum menjadi 100,50 karena jumlah baris sebelumnya (baris pertama) adalah 30,50 dan jumlah baris ini adalah 100,50, sehingga maksimum keduanya adalah 100,50.

Selanjutnya pindah ke baris ketiga, perbandingan akan dilakukan dengan baris kedua sehingga jumlah minimum keduanya adalah 50,00 dan jumlah maksimum kedua baris adalah 100,50.

Fungsi MDX Year to Date (YTD) dan Agregasi Berjalan

MDX adalah bahasa ekspresi multidimensi yang digunakan untuk mengkueri data multidimensi (seperti kubus) dan digunakan dalam solusi intelijen bisnis (BI).

Menurut https://docs.microsoft.com/en-us/sql/mdx/ytd-mdx, fungsi Year to Date (YTD) di MDX bekerja dengan cara yang sama seperti menjalankan atau menggeser agregasi. Misalnya, YTD yang sering digunakan dalam kombinasi tanpa parameter yang disediakan menampilkan total berjalan hingga saat ini.

Ini berarti jika kita menerapkan fungsi ini pada tahun ini memberikan semua data tahun, tetapi jika kita menelusuri hingga Maret itu akan memberi kita semua total dari awal tahun hingga Maret dan seterusnya.

Ini sangat berguna dalam laporan SSRS.

Hal yang Dapat Dilakukan

Itu dia! Anda siap untuk melakukan beberapa analisis data dasar setelah membaca artikel ini dan Anda dapat meningkatkan keterampilan Anda lebih lanjut dengan hal-hal berikut:

  1. Coba tulis skrip agregat yang sedang berjalan dengan membuat jendela di kolom lain seperti Jumlah Total.
  2. Coba juga untuk menulis skrip agregat geser dengan membuat bingkai pada kolom lain seperti Jumlah Total.
  3. Anda dapat menambahkan lebih banyak kolom dan catatan ke tabel (atau bahkan lebih banyak tabel) untuk mencoba kombinasi agregasi lainnya.
  4. Contoh skrip yang disebutkan dalam artikel ini dapat diubah menjadi prosedur tersimpan untuk digunakan dalam laporan SSRS di belakang kumpulan data.

Referensi:

  • Ytd (MDX)
  • dbForge Studio untuk SQL Server

  1. Database
  2.   
  3. Mysql
  4.   
  5. Oracle
  6.   
  7. Sqlserver
  8.   
  9. PostgreSQL
  10.   
  11. Access
  12.   
  13. SQLite
  14.   
  15. MariaDB
  1. Utilitas verifikasi cluster menghasilkan sejumlah besar file xml pada sistem file "/ u01".

  2. FrankenQueries:ketika SQL dan NoSQL bertabrakan

  3. Eksplorasi Mendalam Keamanan Tingkat Baris

  4. SQL GROUP BY Klausa untuk Pemula

  5. Bukan Anda, ini saya (pemecahan masalah I/O)