Apa cara terbaik untuk mendapatkan nilai Max dari kueri LINQ yang mungkin tidak menghasilkan baris? Jika saya lakukan
Dim x = (From y In context.MyTable _
Where y.MyField = value _
Select y.MyCounter).Max
Saya mendapatkan kesalahan saat kueri tidak mengembalikan baris. Saya bisa melakukannya
Dim x = (From y In context.MyTable _
Where y.MyField = value _
Select y.MyCounter _
Order By MyCounter Descending).FirstOrDefault
tapi itu terasa agak tumpul untuk permintaan sesederhana itu. Apakah saya kehilangan cara yang lebih baik untuk melakukannya?
UPDATE: Inilah kisah belakangnya: Saya mencoba mengambil konter kelayakan berikutnya dari tabel anak (sistem warisan, jangan mulai saya ...). Baris kelayakan pertama untuk setiap pasien selalu 1, yang kedua adalah 2, dll. (Jelas ini bukan kunci utama tabel anak). Jadi, saya memilih nilai penghitung maks yang ada untuk seorang pasien, dan kemudian menambahkan 1 padanya untuk membuat baris baru. Ketika tidak ada nilai anak yang ada, saya perlu permintaan untuk mengembalikan 0 (jadi menambahkan 1 akan memberi saya nilai penghitung 1). Perhatikan bahwa saya tidak ingin bergantung pada jumlah mentah baris anak, jika aplikasi warisan memperkenalkan celah dalam nilai penghitung (mungkin). Buruk saya karena mencoba membuat pertanyaan terlalu umum.
sumber
Max
aDateTime
.Max(x => (DateTime?)x.TimeStamp)
masih satu-satunya cara ..Saya baru saja mengalami masalah yang sama, tetapi saya menggunakan metode ekstensi LINQ pada daftar daripada sintaks kueri. Casting untuk trik Nullable juga berfungsi di sana:
sumber
Kedengarannya seperti kasing untuk
DefaultIfEmpty
(kode yang belum diuji mengikuti):sumber
var colCount = RowsEnumerable.Select(row => row.Cols.Count).DefaultIfEmpty().Max()
Pikirkan tentang apa yang Anda tanyakan!
Maks {{1, 2, 3, -1, -2, -3} jelas 3. Maks {{}} jelas 2. Tapi apa maks dari himpunan kosong {}? Jelas itu adalah pertanyaan yang tidak berarti. Maks dari set kosong sama sekali tidak didefinisikan. Mencoba mendapatkan jawaban adalah kesalahan matematis. Maks dari setiap set itu sendiri harus menjadi elemen dalam set itu. Set kosong tidak memiliki elemen, sehingga mengklaim bahwa beberapa angka tertentu adalah maks set itu tanpa berada di set itu adalah kontradiksi matematika.
Sama seperti perilaku yang benar bagi komputer untuk melempar pengecualian ketika programmer memintanya untuk membaginya dengan nol, demikian juga perilaku yang benar bagi komputer untuk melempar pengecualian ketika programmer meminta untuk mengambil maks set kosong. Pembagian dengan nol, mengambil maks set yang kosong, menggoyangkan spacklerorke, dan mengendarai unicorn terbang ke Neverland semuanya tidak ada artinya, tidak mungkin, tidak terdefinisi.
Sekarang, apa yang sebenarnya ingin Anda lakukan?
sumber
Anda selalu bisa menambahkan
Double.MinValue
ke urutan. Ini akan memastikan bahwa setidaknya ada satu elemen danMax
akan mengembalikannya hanya jika itu adalah minimum. Untuk menentukan opsi mana yang lebih efisien (Concat
,FirstOrDefault
atauTake(1)
), Anda harus melakukan pembandingan yang memadai.sumber
Jika daftar memiliki elemen apa pun (mis. Tidak kosong), itu akan mengambil maks bidang MyCounter, yang lain akan mengembalikan 0.
sumber
Karena .Net 3.5 Anda dapat menggunakan DefaultIfEmpty () yang meneruskan nilai default sebagai argumen. Sesuatu seperti salah satu cara berikut:
Yang pertama diizinkan ketika Anda query kolom NOT NULL dan yang kedua adalah cara yang digunakan untuk query kolom NULLABLE. Jika Anda menggunakan DefaultIfEmpty () tanpa argumen, nilai default akan ditentukan untuk tipe output Anda, seperti yang Anda lihat di Tabel Nilai Default .
SELECT yang dihasilkan tidak akan begitu elegan tetapi dapat diterima.
Semoga ini bisa membantu.
sumber
Saya pikir masalahnya adalah apa yang Anda inginkan terjadi ketika kueri tidak memiliki hasil. Jika ini merupakan kasus luar biasa maka saya akan membungkus kueri dalam blok coba / tangkap dan menangani pengecualian yang dihasilkan oleh kueri standar. Jika tidak ada permintaan untuk mengembalikan hasil, maka Anda harus mencari tahu apa yang Anda inginkan hasilnya dalam kasus itu. Mungkin jawaban @ David (atau yang serupa akan bekerja). Artinya, jika MAX akan selalu positif, maka mungkin cukup untuk memasukkan nilai "buruk" yang dikenal ke dalam daftar yang hanya akan dipilih jika tidak ada hasil. Secara umum, saya akan mengharapkan permintaan yang mengambil maksimum untuk memiliki beberapa data untuk bekerja dan saya akan pergi rute mencoba / menangkap karena jika tidak Anda selalu dipaksa untuk memeriksa apakah nilai yang Anda peroleh sudah benar atau tidak. SAYA'
sumber
Kemungkinan lain adalah pengelompokan, mirip dengan cara Anda mendekatinya dalam SQL mentah:
Satu-satunya hal (pengujian lagi di LINQPad) beralih ke rasa VB LINQ memberikan kesalahan sintaks pada klausa pengelompokan. Saya yakin padanan konseptualnya cukup mudah ditemukan, saya hanya tidak tahu bagaimana cara mencerminkannya dalam VB.
SQL yang dihasilkan akan menjadi sesuatu seperti:
SELECT bersarang tampak menjengkelkan, seperti eksekusi kueri akan mengambil semua baris kemudian pilih yang cocok dari set yang diambil ... pertanyaannya adalah apakah SQL Server mengoptimalkan kueri menjadi sesuatu yang sebanding dengan menerapkan klausa mana ke SELECT bagian dalam. Saya sedang melihat itu sekarang ...
Saya tidak berpengalaman dalam menafsirkan rencana eksekusi di SQL Server, tetapi sepertinya ketika klausa WHERE ada di luar SELECT, jumlah baris aktual yang menghasilkan langkah itu adalah semua baris dalam tabel, dibandingkan hanya baris yang cocok. ketika klausa WHERE ada di SELECT bagian dalam. Yang mengatakan, sepertinya hanya 1% biaya dialihkan ke langkah berikut ketika semua baris dipertimbangkan, dan bagaimanapun hanya satu baris yang kembali dari SQL Server jadi mungkin itu bukan perbedaan besar dalam skema besar hal .
sumber
terlambat, tapi saya memiliki masalah yang sama ...
Mengulang kode Anda dari posting asli, Anda ingin maks set S didefinisikan oleh
Mempertimbangkan komentar terakhir Anda
Saya dapat menguraikan kembali masalah Anda sebagai: Anda ingin maks {0 + S}. Dan sepertinya solusi yang diusulkan dengan concat secara semantik adalah yang benar :-)
sumber
Kenapa Bukan sesuatu yang lebih langsung seperti:
sumber
Satu perbedaan menarik yang tampaknya perlu diperhatikan adalah bahwa sementara FirstOrDefault dan Take (1) menghasilkan SQL yang sama (menurut LINQPad,), FirstOrDefault mengembalikan nilai - default - saat tidak ada baris yang cocok dan Take (1) kembali tidak ada hasil ... setidaknya di LINQPad.
sumber
Hanya untuk membuat semua orang di luar sana tahu bahwa menggunakan Linq untuk Entitas metode di atas tidak akan berfungsi ...
Jika Anda mencoba melakukan sesuatu seperti
Ini akan menghasilkan pengecualian:
Saya sarankan hanya melakukan
Dan
FirstOrDefault
akan mengembalikan 0 jika daftar Anda kosong.sumber
sumber
Saya telah mengetuk
MaxOrDefault
metode ekstensi. Tidak banyak, tetapi kehadirannya di Intellisense adalah pengingat yang berguna bahwaMax
pada urutan kosong akan menyebabkan pengecualian. Selain itu, metode ini memungkinkan standar untuk ditentukan jika diperlukan.sumber
Untuk Entity Framework dan Linq to SQL kita dapat mencapai ini dengan mendefinisikan metode ekstensi yang memodifikasi metode yang
Expression
diteruskan keIQueryable<T>.Max(...)
:Pemakaian:
Query yang dihasilkan identik, ini berfungsi seperti panggilan normal ke
IQueryable<T>.Max(...)
metode, tetapi jika tidak ada catatan, ia mengembalikan nilai standar tipe T alih-alih melempar pengecualian.sumber
Saya baru saja mengalami masalah yang sama, tes unit saya lulus menggunakan Max () tetapi gagal ketika dijalankan terhadap database hidup.
Solusi saya adalah memisahkan kueri dari logika yang dilakukan, bukan bergabung dalam satu kueri.
Saya membutuhkan solusi untuk bekerja dalam unit test menggunakan Linq-objects (di Linq-objects Max () bekerja dengan nulls) dan Linq-sql ketika mengeksekusi di lingkungan langsung.
(Saya mengejek Select () di tes saya)
Kurang efisien? Mungkin.
Apakah saya peduli, selama aplikasi saya tidak jatuh di waktu berikutnya? Nggak.
sumber