Saya bekerja dengan sistem pembelian / faktur makanan di MS Access 2013 dan saya mencoba untuk membuat kueri SQL yang akan mengembalikan harga pembelian terbaru untuk setiap item makanan individu.
Berikut adalah diagram dari tabel yang saya kerjakan:
Pemahaman saya tentang SQL sangat mendasar, dan saya mencoba kueri (salah) berikut, dengan harapan akan mengembalikan hanya satu catatan per item (karena DISTINCT
operator) dan hanya akan mengembalikan pembelian terakhir (sejak saya melakukan ORDER BY [Invoice Date] DESC
)
SELECT DISTINCT ([Food items].Item),
[Food items].Item, [Food purchase data].[Price per unit], [Food purchase data].[Purchase unit], Invoices.[Invoice Date]
FROM Invoices
INNER JOIN ([Food items]
INNER JOIN [Food purchase data]
ON [Food items].ID = [Food purchase data].[Food item ID])
ON Invoices.ID = [Food purchase data].[Invoice ID]
ORDER BY Invoices.[Invoice Date] DESC;
Namun kueri di atas hanya mengembalikan semua pembelian makanan (yaitu beberapa catatan untuk setiap catatan di [Food items]
), dengan hasil diurutkan menurun berdasarkan tanggal. Bisakah seseorang menjelaskan kepada saya apa yang saya salah pahami tentang DISTINCT
operator? Artinya, mengapa tidak hanya mengembalikan satu rekaman untuk setiap item [Food items]
?
Dan lebih penting lagi - apa cara paling sederhana bagi saya untuk hanya menarik data pembelian makanan terbaru untuk setiap item makanan, mengingat struktur tabel yang ditunjukkan di atas ? Saya tidak terlalu peduli dengan efisiensi dan kesederhanaannya (basis data yang saya kerjakan agak kecil - itu akan bertahun-tahun sebelum bahkan dalam kisaran puluhan ribu catatan). Saya lebih peduli tentang permintaan yang bisa dimengerti untuk seseorang dengan sedikit pengetahuan tentang SQL.
UPDATE: Jadi saya mencoba, kedua jawaban yang disarankan di bawah ini, dan keduanya tidak bekerja (mereka hanya memunculkan kesalahan sintaksis).
Berdasarkan saran di bawah ini, dan bacaan lebih lanjut online, saya menulis permintaan baru berikut, menggunakan fungsi agregat max()
dan GROUP BY
klausa:
SELECT [Food purchase data].[Food item ID], [Food purchase data].[Price per unit], max(Invoices.[Invoice Date]) AS MostRecentInvoiceDate
FROM [Food purchase data], Invoices
GROUP BY [Food purchase data].[Food item ID], [Food purchase data].[Price per unit];
Tetapi saya masih memiliki masalah yang sama: yaitu, saya masih melihat lebih dari satu hasil untuk setiap item makanan. Adakah yang bisa menjelaskan mengapa permintaan ini tidak hanya mengembalikan pembelian terakhir untuk setiap item makanan?
PEMBARUAN 2 (ASK!) :
Tidak ada jawaban di bawah ini yang berhasil tetapi berdasarkan modifikasi berat dari jawaban Vladimir di bawah ini , saya dapat membuat pertanyaan berikut, yang tampaknya memberikan hasil yang benar.
Pertama, saya membuat tampilan ini dan menamainya "LatestInvoices":
SELECT InvoicesMaxDate.ItemID, InvoicesMaxDate.MaxDate, InvoicesMaxDate.MaxID
FROM [Food purchase data], Invoices, (SELECT [Food purchase data].[Food item ID] AS ItemID, MAX(Invoices.[Invoice Date]) AS MaxDate, MAX(Invoices.[Invoice ID]) AS MaxID
FROM [Food purchase data], Invoices
WHERE Invoices.[Invoice ID] = [Food purchase data].[Invoice ID]
GROUP BY [Food purchase data].[Food item ID]
) AS InvoicesMaxDate
WHERE InvoicesMaxDate.MaxID = [Food purchase data].[Invoice ID] AND
InvoicesMaxDate.ItemID = [Food purchase data].[Food item ID] AND
InvoicesMaxDate.MaxDate = Invoices.[Invoice Date]
GROUP BY InvoicesMaxDate.ItemID, InvoicesMaxDate.MaxDate, InvoicesMaxDate.MaxID
Lalu saya menulis permintaan lain untuk menarik bidang yang saya butuhkan:
SELECT [Food items].ID AS FoodItemID, [Food items].Item AS FoodItem, [Food purchase data].[Price], [Food purchase data].[Price per unit], [Food purchase data].[Purchase unit], LatestInvoices.MaxDate as InvoiceDate
FROM [Food items], [Food purchase data], LatestInvoices
WHERE LatestInvoices.[MaxID] = [Food purchase data].[Invoice ID] AND
LatestInvoices.ItemID = [Food purchase data].[Food item ID] AND
LatestInvoices.ItemID = [Food items].ID
ORDER BY [Food items].Item;
Terima kasih kepada Anda semua yang telah meluangkan waktu untuk membantu saya dengan ini!
DISTINCT
mengembalikan baris yang berbeda di semua kolom di baris, bukan kolom tunggal.[
dan]
ID
kolom, sehinggaID
dalamInvoices
tabel menjadiInvoiceID
.DISTINCT
itu dengan satu kolom. Apakah ada operator analog yang hanya akan memilih berdasarkan keunikan dalam satu kolom? Juga, terima kasih atas tips tentang konvensi penamaan - ya, sangat menjengkelkan harus digunakan di[ ... ]
mana-mana ... Dan saya bisa melihat bagaimana memasukkan nama tabel di kolom ID akan meningkatkan keterbacaan.Jawaban:
Akses MS agak terbatas.
Saya berasumsi bahwa ada kemungkinan untuk memiliki lebih dari satu faktur untuk tanggal yang sama. Dalam hal ini saya akan memilih faktur dengan ID tertinggi.
Pada awalnya kita akan menemukan Tanggal Faktur maksimum untuk setiap Item Makanan.
Karena ada beberapa faktur untuk tanggal maks yang ditemukan, kami akan memilih satu faktur dengan ID maks per Item
Berdasarkan sintaks MS Access dari nested joins dan menggunakan contoh ini dari dokumen:
Mari kita coba menyatukannya:
Sekarang kami memiliki ItemID dan ID dari Faktur terakhir untuk Item itu. Gabung ini ke tabel asli untuk mengambil detail lainnya (kolom).
Dalam praktiknya saya akan membuat tampilan untuk permintaan pertama dengan satu bergabung. Lalu saya akan membuat tampilan kedua yang menggabungkan tampilan pertama dengan tabel, lalu tampilan ketiga dan seterusnya, untuk menghindari gabungan yang bersarang atau meminimalkannya. Permintaan keseluruhan akan lebih mudah dibaca.
Edit untuk mengklarifikasi apa yang saya maksud berdasarkan solusi akhir Anda yang Anda masukkan ke pertanyaan.
Upaya terakhir untuk menyampaikan pesan saya.
Ini yang Anda tulis berdasarkan saran saya di atas:
Inilah yang saya maksud:
Apakah Anda melihat perbedaannya?
Pengembalian
InvoicesMaxDate
MAXInvoice Date
untuk masing-masingFood item ID
. Jika ada dua faktur yang samaFood item ID
dengan MAX yang sama,Invoice Date
kami harus memilih satu faktur di antaranya. Ini dilakukan dengan mengelompokkan berdasarkanInvoicesMaxDate.ItemID, InvoicesMaxDate.MaxDate
. Seharusnya tidak ada pengelompokan diInvoices.[Invoice ID]
sini, karena kami ingin memilih faktur dengan ID maksimum.Setelah kueri ini disimpan sebagai
LatestInvoices
tampilan, kueri ini digunakan lebih lanjut saat Anda menulis dengan benar (perhatikan, bahwa kueri akhir menggunakanLatestInvoices.[Invoice ID]
danLatestInvoices.ItemID
, tetapi tidak menggunakanLatestInvoices.MaxDate
):Adapun, mengapa permintaan terakhir Anda dalam pertanyaan mengembalikan beberapa baris per Item:
Anda mengelompokkan di sini oleh
[Food item ID]
dan[Price per unit]
, jadi Anda akan mendapatkan banyak baris karena ada kombinasi unik dari dua kolom ini.Kueri berikut akan mengembalikan satu baris per
[Food item ID]
.Sebuah catatan, Anda benar-benar harus menggunakan eksplisit
INNER JOIN
bukan,
. Sintaksnya berumur 20 tahun.sumber
"Syntax error (missing operator) in query expression"
ekspresiINNER JOIN Invoices AS I2 ON I2.ID = FPD2.[Invoice ID]
... Saya akan bermain-main dengannya lebih banyak untuk melihat apakah saya bisa membuatnya bekerja.(
dan)
ketika permintaan menggunakan beberapa bergabung dan untuk memindahkanON
klausa sekitar sedikit. Saya tidak memiliki akses untuk memeriksa, tetapi saya dapat mencoba menebak sintaks yang benar dengan membaca dokumen hari ini.LatestInvoices
: finalGROUP
seharusnyaBY InvoicesMaxDate.ItemID, InvoicesMaxDate.MaxDate
hanya, tanpaInvoices.[Invoice ID]
. PadaSELECT
bagian itu harus adaMAX(Invoices.[Invoice ID]) AS [Invoice ID]
. Ini intinya. Pada awalnya (dalam permintaan dalam) kamiGROUP BY [Food item ID]
dan menemukan Tanggal Faktur maksimum. Mungkin ada beberapa faktur dengan tanggal ini, jadi ada satu detikGROUP BY
untuk memilih faktur dengan ID maksimum di antara mereka.ItemID
dengan tanggal besar yang sama dan coba kedua kueri.Kueri yang hanya berfungsi di luar kotak:
sumber
Saya bisa menyelesaikannya dengan pertanyaan berikut:
Karena saya tidak memiliki Access, saya menguji ini pada SQL Server. Saya harap ini akan berhasil untuk Anda.
Edit / Permintaan tambahan : Untuk menambahkan kolom lain dari tabel item makanan saya mengubah permintaan. Saya melakukannya dengan cara yang tidak terlalu saya sukai. Jika itu baik untuk Anda tergantung pada data dan persyaratan Anda. Saya bergabung dengan tabel INVOICES lagi dengan menggunakan Tanggal Pesanan. Jika ini adalah tanggal termasuk waktu saya berolahraga, harap perhatikan hal itu. Saya tidak melihat cara lain dalam skenario Anda. Mungkin ada solusi yang lebih baik menggunakan permintaan rekursif ...?
Silakan coba dan beri tahu saya jika berhasil:
sumber
Item
,Price per unit
, dll)?Saya percaya di bawah ini akan berhasil.
Adapun alasan permintaan Anda tidak mengembalikan hasil yang Anda inginkan:
Masalah terbesar yang saya lihat adalah Anda tidak benar-benar melakukan apa pun untuk bergabung dengan tabel Anda. "Gabung" implisit yang hadir hanya dengan mencantumkan keduanya dalam klausa FROM Anda memberi Anda produk Cartesian. Pada dasarnya itu akan mengembalikan setiap kemungkinan kombinasi di database Anda untuk bidang yang Anda tanyakan.
Misalnya, jika kedua tabel memiliki masing-masing 3 catatan alih-alih mengembalikan tanggal terbaru, kueri Anda akan mengembalikan sesuatu seperti: 1,1 1,2 1,3 2,1 2,2 2,3 3,1 3,2 3 3
Sangat penting bagi Anda untuk secara eksplisit menyatakan bergabung. Dua cara yang dapat Anda lakukan dalam kueri adalah:
ATAU
Kueri yang diperbarui, jika itu masih tidak berhasil coba hapus alias dan gunakan nama kolom yang sepenuhnya memenuhi syarat.
sumber
Saya setuju dengan saran Max tentang model data Anda. Menerapkannya akan membuat SQL Anda lebih mudah dibaca dalam jangka panjang.
Dengan itu, DISTINCT akan menampilkan baris unik. Jadi untuk hanya menampilkan yang terbaru, Anda harus membatasi kolom yang ditampilkan.
Coba sesuatu seperti:
(Terjemahan: Untuk setiap item di toko, tampilkan tanggal faktur terbarunya.)
Anda bisa menyimpan ini sebagai tampilan dan menggunakannya dalam kueri lain seperti halnya sebuah tabel. Jadi, Anda dapat melakukan join batin pada faktur untuk harga pembelian dan bergabung di tabel lain jika Anda membutuhkan detail itu.
(Secara teoritis, Anda juga bisa melakukan kueri bersarang, tetapi karena Anda meminta sederhana, kueri yang disimpan lebih sederhana.)
UPDATE berdasarkan pada pembaruan Anda:
Saya akan menggunakan klausa WHERE daripada GABUNG karena saya tidak memiliki MS Access. Anda harus dapat menggunakan GUI untuk membuat koneksi antara tabel di MS Access berdasarkan informasi ini. (Berikan SQLFiddle jika Anda benar-benar membutuhkan bantuan untuk pemecahan masalah lebih lanjut.)
Langkah 1: Simpan ini sebagai LIHAT (Misalnya, "MostRecentInvoice")
Langkah 2: Gunakan tampilan dalam kueri ke-2
... dan untuk menjawab pertanyaan Anda: Kueri ke-2 dalam pembaruan tidak berfungsi karena kolom [Harga per unit] ada di pernyataan SELECT dan GROUP BY Anda. Ini pada dasarnya berarti Anda meminta untuk melihat SEMUA nilai yang mungkin dari [Harga per unit] meskipun apa yang benar-benar Anda inginkan hanyalah satu: nilai terbaru.
sumber
WHERE [Food purchase data].[Food item ID] = Invoices.ID
... Saya menganggap Anda maksudWHERE [Food purchase data].[Invoice ID] = Invoices.[Invoice ID]
tetapi itu masih mengembalikan beberapa tanggal per item makanan, bukan hanya yang terbaru.