Semantik null kueri
Pendahuluan
Database SQL beroperasi pada logika bernilai 3 (true
, false
, null
) saat melakukan perbandingan, dibandingkan dengan logika boolean C#. Saat menerjemahkan kueri LINQ ke SQL, EF Core mencoba mengkompensasi perbedaan dengan memperkenalkan pemeriksaan null tambahan untuk beberapa elemen kueri.
Untuk mengilustrasikan hal ini, mari kita tentukan entitas berikut:
public class NullSemanticsEntity
{
public int Id { get; set; }
public int Int { get; set; }
public int? NullableInt { get; set; }
public string String1 { get; set; }
public string String2 { get; set; }
}
dan mengeluarkan beberapa kueri:
var query1 = context.Entities.Where(e => e.Id == e.Int);
var query2 = context.Entities.Where(e => e.Id == e.NullableInt);
var query3 = context.Entities.Where(e => e.Id != e.NullableInt);
var query4 = context.Entities.Where(e => e.String1 == e.String2);
var query5 = context.Entities.Where(e => e.String1 != e.String2);
Dua kueri pertama menghasilkan perbandingan sederhana. Dalam kueri pertama, kedua kolom tidak dapat diubah ke null sehingga pemeriksaan null tidak diperlukan. Dalam kueri kedua, NullableInt
dapat berisi null
, tetapi Id
tidak dapat diubah ke null; dibandingkan null
dengan hasil non-null null
sebagai hasilnya, yang akan difilter berdasarkan WHERE
operasi. Jadi tidak ada istilah tambahan yang diperlukan juga.
SELECT [e].[Id], [e].[Int], [e].[NullableInt], [e].[String1], [e].[String2]
FROM [Entities] AS [e]
WHERE [e].[Id] = [e].[Int]
SELECT [e].[Id], [e].[Int], [e].[NullableInt], [e].[String1], [e].[String2]
FROM [Entities] AS [e]
WHERE [e].[Id] = [e].[NullableInt]
Kueri ketiga memperkenalkan pemeriksaan null. null
Kapan NullableInt
perbandingan Id <> NullableInt
menghasilkan null
, yang akan difilter berdasarkan WHERE
operasi. Namun, dari perspektif logika boolean kasus ini harus dikembalikan sebagai bagian dari hasilnya. Oleh karena itu EF Core menambahkan pemeriksaan yang diperlukan untuk memastikan bahwa.
SELECT [e].[Id], [e].[Int], [e].[NullableInt], [e].[String1], [e].[String2]
FROM [Entities] AS [e]
WHERE ([e].[Id] <> [e].[NullableInt]) OR [e].[NullableInt] IS NULL
Kueri empat dan lima memperlihatkan pola saat kedua kolom dapat diubah ke null. Perlu dicatat bahwa operasi menghasilkan kueri yang <>
lebih rumit (dan berpotensi lebih lambat) daripada ==
operasi.
SELECT [e].[Id], [e].[Int], [e].[NullableInt], [e].[String1], [e].[String2]
FROM [Entities] AS [e]
WHERE ([e].[String1] = [e].[String2]) OR ([e].[String1] IS NULL AND [e].[String2] IS NULL)
SELECT [e].[Id], [e].[Int], [e].[NullableInt], [e].[String1], [e].[String2]
FROM [Entities] AS [e]
WHERE (([e].[String1] <> [e].[String2]) OR ([e].[String1] IS NULL OR [e].[String2] IS NULL)) AND ([e].[String1] IS NOT NULL OR [e].[String2] IS NOT NULL)
Perlakuan nilai nullable dalam fungsi
Banyak fungsi dalam SQL hanya dapat mengembalikan null
hasil jika beberapa argumennya adalah null
. EF Core memanfaatkan ini untuk menghasilkan kueri yang lebih efisien.
Kueri di bawah ini mengilustrasikan pengoptimalan:
var query = context.Entities.Where(e => e.String1.Substring(0, e.String2.Length) == null);
SQL yang dihasilkan adalah sebagai berikut (kita tidak perlu mengevaluasi SUBSTRING
fungsi karena hanya akan null ketika salah satu argumen ke dalamnya null.):
SELECT [e].[Id], [e].[Int], [e].[NullableInt], [e].[String1], [e].[String2]
FROM [Entities] AS [e]
WHERE [e].[String1] IS NULL OR [e].[String2] IS NULL
Pengoptimalan juga dapat digunakan untuk fungsi yang ditentukan pengguna. Lihat halaman pemetaan fungsi yang ditentukan pengguna untuk detail selengkapnya.
Menulis kueri berkinerja
Membandingkan kolom yang tidak dapat diubah ke null lebih sederhana dan lebih cepat daripada membandingkan kolom yang dapat diubah ke null. Pertimbangkan untuk menandai kolom sebagai tidak dapat diubah ke null jika memungkinkan.
Memeriksa kesetaraan (
==
) lebih sederhana dan lebih cepat daripada memeriksa non-kesetaraan (!=
), karena kueri tidak perlu membedakan antaranull
danfalse
hasil. Gunakan perbandingan kesetaraan jika memungkinkan. Namun, hanya meniadakan==
perbandingan secara efektif sama!=
dengan , sehingga tidak menghasilkan peningkatan performa.Dalam beberapa kasus, dimungkinkan untuk menyederhanakan perbandingan kompleks dengan memfilter
null
nilai dari kolom secara eksplisit - misalnya ketika tidak adanull
nilai yang ada atau nilai-nilai ini tidak relevan dalam hasilnya. Pertimbangkan contoh berikut:
var query1 = context.Entities.Where(e => e.String1 != e.String2 || e.String1.Length == e.String2.Length);
var query2 = context.Entities.Where(
e => e.String1 != null && e.String2 != null && (e.String1 != e.String2 || e.String1.Length == e.String2.Length));
Kueri ini menghasilkan SQL berikut:
SELECT [e].[Id], [e].[Int], [e].[NullableInt], [e].[String1], [e].[String2]
FROM [Entities] AS [e]
WHERE ((([e].[String1] <> [e].[String2]) OR ([e].[String1] IS NULL OR [e].[String2] IS NULL)) AND ([e].[String1] IS NOT NULL OR [e].[String2] IS NOT NULL)) OR ((CAST(LEN([e].[String1]) AS int) = CAST(LEN([e].[String2]) AS int)) OR ([e].[String1] IS NULL AND [e].[String2] IS NULL))
SELECT [e].[Id], [e].[Int], [e].[NullableInt], [e].[String1], [e].[String2]
FROM [Entities] AS [e]
WHERE ([e].[String1] IS NOT NULL AND [e].[String2] IS NOT NULL) AND (([e].[String1] <> [e].[String2]) OR (CAST(LEN([e].[String1]) AS int) = CAST(LEN([e].[String2]) AS int)))
Di kueri kedua, null
hasil difilter dari String1
kolom secara eksplisit. EF Core dapat dengan aman memperlakukan String1
kolom sebagai tidak dapat diubah ke null selama perbandingan, menghasilkan kueri yang lebih sederhana.
Menggunakan semantik null relasional
Dimungkinkan untuk menonaktifkan kompensasi perbandingan null dan menggunakan semantik null relasional secara langsung. Ini dapat dilakukan dengan memanggil UseRelationalNulls(true)
metode pada pembangun opsi di dalam OnConfiguring
metode:
new SqlServerDbContextOptionsBuilder(optionsBuilder).UseRelationalNulls();
Peringatan
Saat menggunakan semantik null relasional, kueri LINQ Anda tidak lagi memiliki arti yang sama seperti yang mereka lakukan di C#, dan dapat menghasilkan hasil yang berbeda dari yang diharapkan. Berhati-hatilah saat menggunakan mode ini.