Estoy tratando de determinar cómo contar las filas coincidentes en una tabla usando EntityFramework.
El problema es que cada fila puede tener muchos megabytes de datos (en un campo binario). Por supuesto, el SQL sería algo como esto:
SELECT COUNT(*) FROM [MyTable] WHERE [fkID] = '1';
Podría cargar todas las filas y luego encontrar el Conde con:
var owner = context.MyContainer.Where(t => t.ID == '1');
owner.MyTable.Load();
var count = owner.MyTable.Count();
Pero eso es tremendamente ineficiente. ¿Hay alguna forma más simple?
EDITAR: Gracias a todos. He movido la base de datos de un adjunto privado para poder ejecutar la creación de perfiles; esto ayuda pero causa confusiones que no esperaba.
Y mis datos reales son un poco más profundos, usaré camiones que transporten paletas de cajas de artículos , y no quiero que el camión se vaya a menos que haya al menos un artículo en él.
Mis intentos se muestran a continuación. La parte que no entiendo es que CASE_2 nunca accede al servidor de base de datos (MSSQL).
var truck = context.Truck.FirstOrDefault(t => (t.ID == truckID));
if (truck == null)
return "Invalid Truck ID: " + truckID;
var dlist = from t in ve.Truck
where t.ID == truckID
select t.Driver;
if (dlist.Count() == 0)
return "No Driver for this Truck";
var plist = from t in ve.Truck where t.ID == truckID
from r in t.Pallet select r;
if (plist.Count() == 0)
return "No Pallets are in this Truck";
#if CASE_1
/// This works fine (using 'plist'):
var list1 = from r in plist
from c in r.Case
from i in c.Item
select i;
if (list1.Count() == 0)
return "No Items are in the Truck";
#endif
#if CASE_2
/// This never executes any SQL on the server.
var list2 = from r in truck.Pallet
from c in r.Case
from i in c.Item
select i;
bool ok = (list.Count() > 0);
if (!ok)
return "No Items are in the Truck";
#endif
#if CASE_3
/// Forced loading also works, as stated in the OP...
bool ok = false;
foreach (var pallet in truck.Pallet) {
pallet.Case.Load();
foreach (var kase in pallet.Case) {
kase.Item.Load();
var item = kase.Item.FirstOrDefault();
if (item != null) {
ok = true;
break;
}
}
if (ok) break;
}
if (!ok)
return "No Items are in the Truck";
#endif
Y el SQL resultante de CASE_1 se canaliza a través de sp_executesql , pero:
SELECT [Project1].[C1] AS [C1]
FROM ( SELECT cast(1 as bit) AS X ) AS [SingleRowTable1]
LEFT OUTER JOIN (SELECT
[GroupBy1].[A1] AS [C1]
FROM ( SELECT
COUNT(cast(1 as bit)) AS [A1]
FROM [dbo].[PalletTruckMap] AS [Extent1]
INNER JOIN [dbo].[PalletCaseMap] AS [Extent2] ON [Extent1].[PalletID] = [Extent2].[PalletID]
INNER JOIN [dbo].[Item] AS [Extent3] ON [Extent2].[CaseID] = [Extent3].[CaseID]
WHERE [Extent1].[TruckID] = '....'
) AS [GroupBy1] ) AS [Project1] ON 1 = 1
[ Realmente no tengo camiones, conductores, tarimas, cajas o artículos; Como puede ver en el SQL, las relaciones Camión-Palé y Palé-Caja son de muchos a muchos, aunque no creo que eso importe. Mis objetos reales son intangibles y más difíciles de describir, así que cambié los nombres. ]
fuente
Respuestas:
Sintaxis de la consulta:
Sintaxis del método:
Ambos generan la misma consulta SQL.
fuente
SelectMany()
? Es necesario ¿No funcionaría correctamente sin él?MyContainer.Where(o => o.ID == '1')
)Creo que quieres algo como
(editado para reflejar comentarios)
fuente
var count = context.MyTable.Count(t => t.MyContainer.ID == '1');
no larga y fea:var count = (from o in context.MyContainer where o.ID == '1' from t in o.MyTable select t).Count();
pero depende del estilo de codificación ...Según tengo entendido, la respuesta seleccionada aún carga todas las pruebas relacionadas. Según este blog de msdn, hay una forma mejor.
http://blogs.msdn.com/b/adonet/archive/2011/01/31/using-dbcontext-in-ef-feature-ctp5-part-6-loading-related-entities.aspx
Específicamente
fuente
Find(1)
solicitud adicional . Simplemente cree la entidad y adjúntela al contexto:var princess = new PrincessEntity{ Id = 1 }; context.Princesses.Attach(princess);
Este es mi codigo:
Asegúrese de que la variable esté definida como IQueryable, luego, cuando use el método Count (), EF ejecutará algo como
De lo contrario, si los registros se definen como IEnumerable, el sql generado consultará toda la tabla y contará las filas devueltas.
fuente
Bueno, incluso
SELECT COUNT(*) FROM Table
será bastante ineficiente, especialmente en tablas grandes, ya que SQL Server realmente no puede hacer nada más que realizar un escaneo completo de la tabla (escaneo de índice agrupado).A veces, es suficientemente bueno saber un número aproximado de filas de la base de datos y, en tal caso, una declaración como esta podría ser suficiente:
Esto inspeccionará la vista de administración dinámica y extraerá el número de filas y el tamaño de la tabla, dada una tabla específica. Lo hace sumando las entradas para el montón (index_id = 0) o el índice agrupado (index_id = 1).
Es rápido, fácil de usar, pero no se garantiza que sea 100% preciso o esté actualizado. Pero en muchos casos, esto es "suficientemente bueno" (y supone una carga mucho menor para el servidor).
¿Quizás eso también funcionaría para ti? Por supuesto, para usarlo en EF, tendría que resumir esto en un proceso almacenado o usar una llamada directa "Ejecutar consulta SQL".
Bagazo
fuente
Utilice el método ExecuteStoreQuery del contexto de la entidad. Esto evita descargar todo el conjunto de resultados y deserializar en objetos para hacer un simple recuento de filas.
fuente
int count = context.MyTable.Count(m => m.MyContainerID == '1')
, el SQL generado se parecerá exactamente a lo que está haciendo, pero el código es mucho mejor. Ninguna entidad se carga en la memoria como tal. Pruébelo en LINQPad si lo desea; le mostrará el SQL utilizado debajo de las cubiertas.Creo que esto debería funcionar...
fuente