SQLite按位查询返回异常结果排查(EF Core 7)
为何EF Core 7生成的SQLite查询结果偏差巨大?
问题背景
我用EF Core 7编写了如下查询:
Me.Context.Patients.Where(Function(Patient) Patient.BloodType.ReceivesFrom.HasFlag(BloodType))
生成的原生SQL为:
SELECT "p"."Id", "p"."BloodTypeId", "p"."CityId", "p"."Code", "p"."Email", "p"."FirstName", "p"."LastName", "p"."ZipCodeId", "b"."Type" FROM "Patients" AS "p" INNER JOIN "BloodTypes" AS "b" ON "p"."BloodTypeId" = "b"."Id" WHERE ("b"."ReceivesFrom" & 4) = 4
查询返回结果存在严重偏差:
| Id | BloodTypeId | CityId | Code | FirstName | LastName | ZipCodeId | Type | |
|---|---|---|---|---|---|---|---|---|
| 1 | 4 | 46 | U6UJI | oliver.runte@gmail.com | Abbigail | Cremin | 12 | 4 |
| 2 | 5 | 88 | LV892 | eunice81@gmail.com | Donnell | McKenzie | 94 | 8 |
| 5 | 8 | 44 | 5F6TN | susie.schaefer@gmail.com | Nedra | Stokes | 66 | 64 |
| 7 | 5 | 16 | 6LQ61 | eileen76@gmail.com | Jairo | Jakubowski | 69 | 8 |
| 10 | 5 | 8 | BI56C | kristopher46@yahoo.com | Modesto | Reichert | 33 | 8 |
| 11 | 5 | 94 | R8LP2 | connie_king@gmail.com | Raphaelle | Murphy | 47 | 8 |
| 16 | 5 | 66 | 8TC6V | amber62@hotmail.com | Daphney | Bechtelar | 94 | 8 |
| 17 | 4 | 45 | DM52B | lucas_mraz@yahoo.com | Durward | Larson | 67 | 4 |
根据血型规则,A+(枚举值4)可接受A+/-、O+/-型血捐赠,对应BloodTypeId应为2、3、4、5,测试数据中符合条件的有11条记录,但当前结果包含无效记录(如ID5的AB+型,Type为64),且遗漏了PatientId为4、6、15、19的有效记录。
相关数据
BloodTypes表数据
| Id | ReceivesFrom | DonatesTo | Type |
|---|---|---|---|
| 1 | 0 | 0 | 0 |
| 2 | 3 | 255 | 1 |
| 3 | 3 | 255 | 2 |
| 4 | 15 | 204 | 4 |
| 5 | 15 | 204 | 8 |
| 6 | 51 | 240 | 16 |
| 7 | 51 | 240 | 32 |
| 8 | 255 | 192 | 64 |
| 9 | 255 | 192 | 128 |
Flags枚举定义
<Flags> Public Enum BloodTypes As Byte <Description("?")> Unknown = 0 <Description("O+")> OPositive = 1 <Description("O-")> ONegative = 2 <Description("A+")> APositive = 4 <Description("A-")> ANegative = 8 <Description("B+")> BPositive = 16 <Description("B-")> BNegative = 32 <Description("AB+")> ABPositive = 64 <Description("AB-")> ABNegative = 128 End Enum
Patients测试数据
| Id | BloodTypeId | CityId | Code | FirstName | LastName | ZipCodeId | |
|---|---|---|---|---|---|---|---|
| 1 | 4 | 46 | U6UJI | oliver.runte@gmail.com | Abbigail | Cremin | 12 |
| 2 | 5 | 88 | LV892 | eunice81@gmail.com | Donnell | McKenzie | 94 |
| 3 | 6 | 85 | SNTB4 | kristi_bergnaum26@gmail.com | Paige | Watsica | 4 |
| 4 | 2 | 24 | 4XMHO | claire97@gmail.com | Gregg | Wilkinson | 26 |
| 5 | 8 | 44 | 5F6TN | susie.schaefer@gmail.com | Nedra | Stokes | 66 |
| 6 | 2 | 88 | 6C1LG | bridget88@yahoo.com | Brice | Runolfsson | 55 |
| 7 | 5 | 16 | 6LQ61 | eileen76@gmail.com | Jairo | Jakubowski | 69 |
| 8 | 6 | 12 | 50U3K | angelina62@yahoo.com | Fredy | Heathcote | 54 |
| 9 | 7 | 2 | DD4YB | teri41@yahoo.com | Dorothea | Littel | 20 |
| 10 | 5 | 8 | BI56C | kristopher46@yahoo.com | Modesto | Reichert | 33 |
| 11 | 5 | 94 | R8LP2 | connie_king@gmail.com | Raphaelle | Murphy | 47 |
| 12 | 7 | 92 | AALG8 | janice67@gmail.com | Willy | Pouros | 28 |
| 13 | 7 | 82 | LCH1T | dwayne_legros@yahoo.com | Everardo | Lang | 6 |
| 14 | 6 | 8 | 8HNN2 | patrick_gleichner78@hotmail.com | Koby | Purdy | 53 |
| 15 | 3 | 24 | FWG31 | irene.prosacco@hotmail.com | Gardner | Mueller | 27 |
| 16 | 5 | 66 | 8TC6V | amber62@hotmail.com | Daphney | Bechtelar | 94 |
| 17 | 4 | 45 | DM52B | lucas_mraz@yahoo.com | Durward | Larson | 67 |
| 18 | 7 | 57 | 4750K | levi_lang64@yahoo.com | Joelle | Bashirian | 58 |
| 19 | 2 | 18 | O8Z0Q | marguerite4@yahoo.com | Loy | Ratke | 98 |
| 20 | 1 | 6 | 8UKAB | paulette.moore8@hotmail.com | Christa | Gibson | 48 |
BloodTypes表定义
CREATE TABLE "BloodTypes" ( "Id" INTEGER NOT NULL, "ReceivesFrom" INTEGER NOT NULL DEFAULT 0, "DonatesTo" INTEGER NOT NULL DEFAULT 0, "Type" INTEGER NOT NULL DEFAULT 0, CONSTRAINT "PK_BloodTypes" PRIMARY KEY("Id" AUTOINCREMENT) );
核心原因分析
问题出在查询逻辑与需求完全倒置:
- 当前查询的逻辑是:筛选
患者的血型能接受的捐赠类型包含A+(枚举值4),对应SQL中的("b"."ReceivesFrom" & 4) = 4,即检查患者的ReceivesFrom是否包含A+的标记。 - 实际需求是:筛选
患者的血型属于A+能接受的捐赠类型,也就是A+可以接受O+、O-、A+、A-(对应枚举值1、2、4、8),需要检查的是A+的ReceivesFrom是否包含患者的血型Type,而非反过来。
结合BloodTypes表数据验证:
- 符合需求的BloodTypeId2(O+,Type1)、3(O-,Type2)的
ReceivesFrom是3(仅包含1、2),不包含4,因此不会被当前查询返回; - 无效的BloodTypeId8(AB+,Type64)的
ReceivesFrom是255(包含所有枚举值),自然包含4,因此被错误返回; - BloodTypeId4(A+)、5(A-)的
ReceivesFrom是15(包含1、2、4、8),包含4,因此被正确返回,但遗漏了O+、O-的患者。
EF Core生成的SQL位运算逻辑本身无问题,SQLite的&位运算执行正确,结果偏差完全来自业务逻辑的倒置。
修正方案
调整查询逻辑,改为检查目标血型(A+)的ReceivesFrom是否包含患者的血型Type:
' 先获取A+血型的ReceivesFrom值 Dim targetReceivesFrom = Me.Context.BloodTypes.First(Function(b) b.Type = BloodTypes.APositive).ReceivesFrom ' 筛选患者血型Type被包含在targetReceivesFrom中的记录 Dim result = Me.Context.Patients.Where(Function(p) targetReceivesFrom.HasFlag(p.BloodType.Type)).ToList()
对应的正确SQL为:
SELECT "p"."Id", "p"."BloodTypeId", "p"."CityId", "p"."Code", "p"."Email", "p"."FirstName", "p"."LastName", "p"."ZipCodeId", "b"."Type" FROM "Patients" AS "p" INNER JOIN "BloodTypes" AS "b" ON "p"."BloodTypeId" = "b"."Id" WHERE (15 & "b"."Type") = "b"."Type"
(注:15是A+的ReceivesFrom值,对应枚举值1|2|4|8)
这样就能得到期望的结果:包含BloodTypeId2、3、4、5的所有患者,排除无效的AB+型患者。
内容的提问来源于stack exchange,提问作者InteXX
相关产品推荐
相关产品推荐

