c# - first - ¿Cómo COUNT filas dentro de EntityFramework sin cargar contenido?
entity framework model first (7)
Bueno, incluso el SELECT COUNT(*) FROM Table
será bastante ineficiente, especialmente en tablas grandes, ya que SQL Server realmente no puede hacer otra cosa que hacer una exploración de tabla completa (exploración de índice en clúster).
A veces, es suficiente saber una cantidad aproximada de filas de la base de datos, y en tal caso, una declaración como esta podría ser suficiente:
SELECT
SUM(used_page_count) * 8 AS SizeKB,
SUM(row_count) AS [RowCount],
OBJECT_NAME(OBJECT_ID) AS TableName
FROM
sys.dm_db_partition_stats
WHERE
OBJECT_ID = OBJECT_ID(''YourTableNameHere'')
AND (index_id = 0 OR index_id = 1)
GROUP BY
OBJECT_ID
Esto inspeccionará la vista de administración dinámica y extraerá el número de filas y el tamaño de la tabla de ella, 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, es fácil de usar, pero no se garantiza que sea 100% exacto o actualizado. Pero en muchos casos, esto es "lo suficientemente bueno" (y pone mucha menos carga en el servidor).
¿Tal vez eso también funcionaría para ti? Por supuesto, para usarlo en EF, tendrías que resumirlo en un proceso almacenado o usar una llamada directa de "Ejecutar consulta SQL".
Bagazo
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 conteo con:
var owner = context.MyContainer.Where(t => t.ID == ''1'');
owner.MyTable.Load();
var count = owner.MyTable.Count();
Pero eso es extremadamente ineficiente. ¿Hay alguna forma más simple?
EDITAR: Gracias, todo. He movido el DB de un adjunto privado para poder ejecutar el perfilado; esto ayuda pero causa confusiones que no esperaba.
Y mis datos reales son un poco más profundos, usaré camiones con palets 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 tiene acceso al servidor de bases 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, paletas, cajas o artículos; Como puede ver en el SQL, las relaciones entre Truck-Pallet y Pallet-Case son muchas, aunque no creo que eso importe. Mis objetos reales son intangibles y más difíciles de describir, así que cambié los nombres. ]
Creo que esto debería funcionar...
var query = from m in context.MyTable
where m.MyContainerId == ''1'' // or what ever the foreign key name is...
select m;
var count = query.Count();
Creo que quieres algo así como
var count = context.MyTable.Count(t => t.MyContainer.ID == ''1'');
(editado para reflejar los comentarios)
Este es mi código:
IQueryable<AuctionRecord> records = db.AuctionRecord;
var count = records.Count();
Asegúrese de que la variable esté definida como IQueryable y luego cuando use el método Count (), EF ejecutará algo así como
select count(*) from ...
De lo contrario, si los registros se definen como IEnumerable, el SQL generado consultará la tabla completa y se contarán las filas.
Según lo entiendo, la respuesta seleccionada todavía carga todas las pruebas relacionadas. De acuerdo con este blog msdn, hay una mejor manera.
Específicamente
using (var context = new UnicornsContext())
var princess = context.Princesses.Find(1);
// Count how many unicorns the princess owns
var unicornHaul = context.Entry(princess)
.Collection(p => p.Unicorns)
.Query()
.Count();
}
Sintaxis de consulta:
var count = (from o in context.MyContainer
where o.ID == ''1''
from t in o.MyTable
select t).Count();
Sintaxis del método:
var count = context.MyContainer
.Where(o => o.ID == ''1'')
.SelectMany(o => o.MyTable)
.Count()
Ambos generan la misma consulta SQL.
Use el método ExecuteStoreQuery del contexto de la entidad. Esto evita descargar todo el conjunto de resultados y deserializar en objetos para hacer un recuento de filas simple.
int count;
using (var db = new MyDatabase()){
string sql = "SELECT COUNT(*) FROM MyTable where FkId = {0}";
object[] myParams = {1};
var cntQuery = db.ExecuteStoreQuery<int>(sql, myParams);
count = cntQuery.First<int>();
}