Entitiy Framework parametrize List<long> - sql-server

We are using EF 6.2 for .NET. We are trying the next query:
dbContext.memberScales
.Where(s => members.Contains(s.idMember))
.OrderByDescending(s => s.dateScale)
.Select(s => new MemberScaleBasicFields
{
id = s.idMember,
dateScale = s.dateScale,
weight = s.weight,
massMuscle = s.massMuscle,
massFat = s.massFat,
massBone = s.massBone,
imc = s.imc,
water = s.water,
dailyCalories = s.dailyCalories,
tmd = s.tmd,
physicalValuation = s.physicalValuation,
adiposity = s.adiposity,
assessment = s.assessment,
ageMetabolica = s.ageMetabolica
})
.ToList();
In the where clause, we have to filter for a list of long.
members.Contains(s.idMember)
When we see the query text, we get a query with hardcore values:
SELECT
[Extent1].[ValoracionFisica] AS [ValoracionFisica],
[Extent1].[IdSocio] AS [IdSocio],
[Extent1].[Fecha] AS [Fecha],
[Extent1].[Peso] AS [Peso],
[Extent1].[MasaMagra] AS [MasaMagra],
[Extent1].[MasaGrasa] AS [MasaGrasa],
[Extent1].[MasaOsea] AS [MasaOsea],
[Extent1].[IMC] AS [IMC],
[Extent1].[Agua] AS [Agua],
[Extent1].[CaloriasDiarias] AS [CaloriasDiarias],
[Extent1].[TMB] AS [TMB],
[Extent1].[Adiposidad] AS [Adiposidad],
[Extent1].[Valoracion] AS [Valoracion],
[Extent1].[EdadMetabolica] AS [EdadMetabolica]
FROM [dbo].[Socios_Bascula] AS [Extent1]
WHERE [Extent1].[IdSocio] IN (cast(1225789 as bigint), cast(1228549 as bigint), cast(1228557 as bigint), cast(1230732 as bigint)....
We want to know how to make this query parametrized. Or if there is any alternative to make this query without dropping cache plan. For example,
[Extent1].[IdSocio] IN (#value1, #value2...)
If we try make the
members.Any(x => x == s.idMember)
we get the next text
SELECT
[Extent1].[ValoracionFisica] AS [ValoracionFisica],
[Extent1].[IdSocio] AS [IdSocio],
[Extent1].[Fecha] AS [Fecha],
[Extent1].[Peso] AS [Peso],
[Extent1].[MasaMagra] AS [MasaMagra],
[Extent1].[MasaGrasa] AS [MasaGrasa],
[Extent1].[MasaOsea] AS [MasaOsea],
[Extent1].[IMC] AS [IMC],
[Extent1].[Agua] AS [Agua],
[Extent1].[CaloriasDiarias] AS [CaloriasDiarias],
[Extent1].[TMB] AS [TMB],
[Extent1].[Adiposidad] AS [Adiposidad],
[Extent1].[Valoracion] AS [Valoracion],
[Extent1].[EdadMetabolica] AS [EdadMetabolica]
FROM [dbo].[Socios_Bascula] AS [Extent1]
WHERE EXISTS (SELECT
1 AS [C1]
FROM (SELECT
cast(1225789 as bigint) AS [C1]
FROM ( SELECT 1 AS X ) AS [SingleRowTable1]
UNION ALL
SELECT
cast(1228549 as bigint) AS [C1]
FROM ( SELECT 1 AS X ) AS [SingleRowTable2]....

Related

How to optimize LINQ to Entities retrieving top x records from a navigation property

Let's suppose we have to retrieve from the DB one record from BlogPost (Id = 123) and its most 5 recent comments, we would probably write something like:
var test = _db.BlogPost.Select(x => new blogDTO()
{
Id = x.Id,
PostText = x.PostText,
Comments = x.Comments.OrderByDescending(o => o.Ts).Take(5).Select(c => new commentDTO() { Text = c.CommentText }).ToList()
}).Where(x => x.Id == 123).ToList();
This would be translated in a very inefficient SQL:
SELECT [i].[Id], [i].[PostText], [t0].[CommentText], [t0].[Id]
FROM [BlogPost] AS [i]
LEFT JOIN (
SELECT [t].[CommentText], [t].[Id], [t].[BlogPostId], [t].[Ts]
FROM (
SELECT [i0].[CommentText] AS [CommentText], [i0].[Id], [i0].[BlogPostId], ROW_NUMBER() OVER(PARTITION BY [i0].[BlogPostId] ORDER BY [i0].[Ts] DESC) AS [row], [i0].[Ts]
FROM [Comments] AS [i0]
) AS [t]
WHERE [t].[row] <= 5
) AS [t0] ON [i].[Id] = [t0].[BlogPostId]
WHERE [i].[Id] = 813
ORDER BY [i].[Id], [t0].[BlogPostId], [t0].[Ts] DESC
While probably the SQL should read something like:
SELECT Id, CommentText, Ts from (
SELECT [i].[Id], ic.CommentText, ROW_NUMBER() OVER(PARTITION BY [ic].[BlogPostId] ORDER BY [ic].[Ts] DESC) AS [row], [ic].[Ts]
FROM [BlogPost] AS [i]
left join Comments as ic on ic.BlogPostId = i.Id
WHERE [i].[Id] = 813 ) as res
where res.row <= 5
Do you know how the LINQ to Entities code could be better written?
Or do you feel this is something the EF team should look into?

SQL Query running slow on the DB server

Execution plan is attached
SELECT a.*, k.UserPrivileges, k.Type
FROM NotifyInterests2 a, KUAF k
WHERE a.UserID = k.ID
AND EXISTS (SELECT ID FROM KUAF WHERE a.UserID = ID AND NOT Type IN (1, 2, 4))
AND (a.NodeID IN ( SELECT 0 UNION ALL ( SELECT DISTINCT( -1 * ID )
FROM KUAF WHERE Type = 5 ) UNION ALL ( SELECT DISTINCT AncestorID
FROM DTreeAncestors
WHERE Exists ( SELECT EventInt2 FROM LLEventQueue
WHERE EventHandlerID = 9001 AND EventSeqNo <=45075882 AND EventInt2 = DataID
UNION ALL SELECT EventInt2 * -1 FROM LLEventQueue
WHERE EventHandlerID = 9001
AND EventSeqNo <=45075882 AND EventInt2 = DataID ))))
that's quite the query =) Tip: try to 'group' the parts of your code for readability; it will make your life a 100 times easier; now while you're writing this and even more in the future when you're maintaining this =)
Some questions remain:
What do you call 'slow'?
How many records are present in each table?
How many records are returned by the query?
What are the indexes on the tables? (sp_helpindex)
Also, it probably would have been helpful to see the entire query plan, but one thing that stands out immediately is that KEAF doesn't seem to have clustered index. I presume it's thus also missing a Primary Key? As you have a field ID there I'd expect that this uniquely identifies each record? If so, add a PK to the table and check what effect this has on the entire query.
I've tried to re-arrange things a bit for readability and I ended up with below. I've gambled on some of the aliases as they are missing in the original query.
SELECT a.*, k.UserPrivileges, k.Type
FROM NotifyInterests2 a
JOIN KUAF k
ON k.ID = a.UserID
WHERE EXISTS ( SELECT kx.ID
FROM KUAF kx
WHERE kx.ID = a.UserID
AND NOT kx.Type IN (1, 2, 4) )
AND a.NodeID IN ( SELECT 0
UNION ALL
SELECT DISTINCT( -1 * k5.ID )
FROM KUAF k5
WHERE k5.Type = 5
UNION ALL
SELECT DISTINCT da.AncestorID
FROM DTreeAncestors da
WHERE EXISTS ( SELECT l1.EventInt2
FROM LLEventQueue l1
WHERE l1.EventHandlerID = 9001
AND l1.EventSeqNo <= 45075882
AND l1.EventInt2 = da.DataID
UNION ALL
SELECT l2.EventInt2 * -1
FROM LLEventQueue l2
WHERE l2.EventHandlerID = 9001
AND l2.EventSeqNo <= 45075882
AND l2.EventInt2 = da.DataID )
)
Some parts can be simplified I think.
This doesn't really make sense:
SELECT DISTINCT da.AncestorID
FROM DTreeAncestors da
WHERE EXISTS ( SELECT l1.EventInt2
FROM LLEventQueue l1
WHERE l1.EventHandlerID = 9001
AND l1.EventSeqNo <= 45075882
AND l1.EventInt2 = da.DataID
UNION ALL
SELECT l2.EventInt2 * -1
FROM LLEventQueue l2
WHERE l2.EventHandlerID = 9001
AND l2.EventSeqNo <= 45075882
AND l2.EventInt2 = da.DataID )
You're simply doing the same query twice in the WHERE EXISTS() part.
Also, as this part belongs to a WHERE IN (...) construction there's no real need for the DISTINCT. In fact, it's probably easier to convert it to a WHERE EXISTS() construction. Something like this:
AND ( a.NodeID = 0
OR EXISTS (SELECT *
FROM KUAF k5
WHERE k5.Type = 5
AND ( -1 * k5.ID ) = a.node_id)
OR EXISTS ( SELECT *
FROM DTreeAncestors da
WHERE da.AncestorID = a.NodeID
AND EXISTS ( SELECT *
FROM LLEventQueue l1
WHERE l1.EventHandlerID = 9001
AND l1.EventSeqNo <= 45075882
AND l1.EventInt2 = da.DataID )
)
Normally I'm no fan of OR but I think that in this case it might work.
So I end up with:
SELECT a.*, k.UserPrivileges, k.Type
FROM NotifyInterests2 a
JOIN KUAF k
ON k.ID = a.UserID
WHERE EXISTS ( SELECT kx.ID
FROM KUAF kx
WHERE a.UserID = kx.ID
AND NOT kx.Type IN (1, 2, 4) )
AND ( a.NodeID = 0
OR EXISTS (SELECT *
FROM KUAF k5
WHERE k5.Type = 5
AND ( -1 * k5.ID ) = a.node_id)
OR EXISTS ( SELECT *
FROM DTreeAncestors da
WHERE da.AncestorID = a.NodeID
AND EXISTS ( SELECT *
FROM LLEventQueue l
WHERE l.EventHandlerID = 9001
AND l.EventSeqNo <= 45075882
AND l.EventInt2 = da.DataID )
)
)
Give this a try and check the query plan. If it still is too slow we'll need to look at the indexes that are on the bespoken tables to see where we can optimize those.
You didn't mention if and which indexes exist in the table.
I would try creating the following indexes and see if there is any performance gain:
CREATE INDEX kuaf_idx_id_type ON KUAF (ID,Type);
CREATE INDEX kuaf_idx_type ON KUAF (Type);
CREATE INDEX lleventqueue_idx_eventha_eventin_dataid_eventse ON LLEventQueue (EventHandlerID,EventInt2,DataID,EventSeqNo);

How will SQL Server execute this WITH clause

I have the following query. Here 'B' is giving me non repeated MessageIds and i am using them in subquery with A, B is internally using A.
So when i call SELECT in the last part, will it execute A again or will it use data already fetched while B was being called ?
WITH A as (
SELECT z.Name, ze.Inside, ze.MessageId, ze.DateTime
FROM ZoneStateEntries ze
INNER JOIN Zone z
ON ze.ZoneId = z.ZoneId
WHERE ze.ObjectId = 1324
AND (Inside = 1 OR Inside = 0)
AND ze.DateTime BETWEEN '2018-10-22 00:00:00' AND '2019-11-05 00:00:00'
),
B as (
SELECT a.MessageId
FROM A a
INNER JOIN A b
on a.MessageId = b.MessageId
GROUP BY a.MessageId
HAVING COUNT(a.MessageId) = 1
)
SELECT *
FROM A
WHERE MessageId IN (
SELECT *
FROM B
)
AND a.Inside = 1
ORDER BY DateTime DESC
The data here is huge and we can't afford to execute query A multiple times. Can we optimize it further?
A a INNER JOIN A b looks like unnecessary. You can use COUNT(DISTINCT MessageId) for getting unique messages.
WITH A as (
SELECT z.Name, ze.Inside, ze.MessageId, ze.DateTime
FROM ZoneStateEntries ze
INNER JOIN Zone z
ON ze.ZoneId = z.ZoneId
WHERE ze.ObjectId = 1324
AND (Inside = 1 OR Inside = 0)
AND ze.DateTime BETWEEN '2018-10-22 00:00:00' AND '2019-11-05 00:00:00'
)
SELECT *
FROM A
WHERE MessageId IN (
SELECT MessageId
FROM A
GROUP BY MessageId
HAVING COUNT(DISTINCT MessageId) = 1
)
AND a.Inside = 1
ORDER BY DateTime DESC

Adding results from two queries

I'm using MS-SQL 2008 R2.
I have 2 Queries which are returning the required results.
But I need to add the two results from each queries to provide a final value [Enterprise Value]. I'm sure this is very straight forward but I'm going round in circles on this, have tried incorporating SUM which I think is the right approach?
Here is the full query as it currently stands:
declare #d1 datetime='2015-12-22'
(select
c.fs_perm_sec_id,
((c.p_price * s.p_com_shs_out)/1000) as [Enterprise Value]
from fp_v1.fp_basic_bd c
left join edm_v1.edm_security_entity_map e
on e.fs_perm_sec_id= c.fs_perm_sec_id
left join fp_v1.fp_basic_sho s
on s.fs_perm_sec_id = c.fs_perm_sec_id
and c.date=#d1
where s."date" =
(
select MAX(s2."date")
from fp_v1.fp_basic_sho s2
where s2.fs_perm_sec_id=c.fs_perm_sec_id
and s2."date" <= c."date"
)
and c."date"=#d1
and e.termination_date is null
and c.fs_perm_sec_id = 'GPHC8W-S-GB')
UNION ALL
select
ff.fs_perm_sec_id,
((FF_debt + ff_pfd_stk + ff_min_int_accum) - FF.ff_cash_st) as [Enterprise Value]
from ff_v2.ff_basic_af_v2 FF
where FF."date" =
( select MAX(FF2."date")
from ff_v2.ff_basic_af_v2 FF2
where FF2.fs_perm_sec_id=FF.fs_perm_sec_id
and FF.date <= FF2.date
)
and FF.fs_perm_sec_id =('GPHC8W-S-GB')
When inserting a "UNION ALL" between the two queries I get the following results:
fs_perm_sec_id Enterprise Value
GPHC8W-S-GB 9270.5204655
GPHC8W-S-GB 835
What I would like to achieve is a sum of the two values brought onto one row, i.e.:
fs_perm_sec_id Enterprise Value
GPHC8W-S-GB 10105.52
Thanks for your help.
Final SQL:
declare #d1 datetime='2015-12-23'
Select fs_perm_sec_id, SUM([Enterprise Value]) AS 'Enterprise Value'
from
(
(select
c.fs_perm_sec_id,
((c.p_price * s.p_com_shs_out)/1000) as [Enterprise Value]
from fp_v1.fp_basic_bd c
left join edm_v1.edm_security_entity_map e
on e.fs_perm_sec_id= c.fs_perm_sec_id
left join fp_v1.fp_basic_sho s
on s.fs_perm_sec_id = c.fs_perm_sec_id
and c.date=#d1
where s."date" =
(
select MAX(s2."date")
from fp_v1.fp_basic_sho s2
where s2.fs_perm_sec_id=c.fs_perm_sec_id
and s2."date" <= c."date"
)
and c."date"=#d1
and e.termination_date is null
and c.fs_perm_sec_id in ('FT9TC5-S-GB','GPHC8W-S-GB','R85KLC-S-US'))
UNION ALL
select
ff.fs_perm_sec_id,
((FF_debt + ff_pfd_stk + ff_min_int_accum) - FF.ff_cash_st) as [Enterprise Value]
from ff_v2.ff_basic_af_v2 FF
where FF."date" =
( select MAX(FF2."date")
from ff_v2.ff_basic_af_v2 FF2
where FF2.fs_perm_sec_id=FF.fs_perm_sec_id
and FF.date <= FF2.date
)
and FF.fs_perm_sec_id in ('FT9TC5-S-GB','GPHC8W-S-GB','R85KLC-S-US')) t
group by t.fs_perm_sec_id
just use the Derived Table and Group by
Select fs_perm_sec_id,
SUM(Enterprise Value) EnterpriseValue
from (**your whole code**)
GROUP BY fs_perm_sec_id
use group by
How to use group by with union in t-sql
SELECT id,sum(*)
FROM ( SELECT id,
time
FROM dbo.a
UNION
SELECT id,
time
FROM dbo.b
)
GROUP BY id
DECLARE
#d1 DATE = '20151222'
, #fs_perm_sec_id VARCHAR(100) = 'GPHC8W-S-GB'
SELECT #fs_perm_sec_id, SUM([Enterprise Value])
FROM (
SELECT [Enterprise Value]
FROM (
SELECT
c.fs_perm_sec_id
, (c.p_price * s.p_com_shs_out) / 1000 AS [Enterprise Value]
, RowNum = ROW_NUMBER() OVER (ORDER BY s.[date] DESC)
from fp_v1.fp_basic_bd c
join fp_v1.fp_basic_sho s on s.fs_perm_sec_id = c.fs_perm_sec_id
left join edm_v1.edm_security_entity_map e on e.fs_perm_sec_id= c.fs_perm_sec_id
where c.[date] = #d1
and e.termination_date is null
and c.fs_perm_sec_id = #fs_perm_sec_id
) t
WHERE t.RowNum = 1
UNION ALL
SELECT FF_debt + ff_pfd_stk + ff_min_int_accum - ff_cash_st
FROM (
SELECT
ff.fs_perm_sec_id
, FF_debt
, ff_pfd_stk
, ff_min_int_accum
, FF.ff_cash_st
, RowNum = ROW_NUMBER() OVER (ORDER BY FF.[date] DESC)
FROM ff_v2.ff_basic_af_v2 FF
WHERE FF.[date] =
AND FF.fs_perm_sec_id = #fs_perm_sec_id
) t
WHERE t.RowNum = 2
) t

how to convert the below subquery into joins using one update statement

Below is a complete query I have and the ultimate aim is to update the claim table. But it should be only one statement without any subquery, only joins are allowed because I am going to run this in an appliance which won't support subquery:
DECLARE #DecWdrwn as TABLE(CtryId smallint, CmId int, DecWdrwnDt int);
WITH s AS
(
SELECT
Ctryid,CmId,Dt,
ISNULL((
SELECT max(CmHistDtTmId)
FROM ClaimHistory l
WHERE St = 3
AND l.Ctryid = c.Ctryid
AND l.CmId = c.CmId)
, 0) MaxDec,
ISNULL((
SELECT max(CmHistDtTmId)
FROM ClaimHistory l
WHERE St = 7
AND l.Ctryid = c.Ctryid
AND l.CmId = c.CmId)
, 0) MaxSet
FROM
ClaimHistory c
WHERE
St =3
)
INSERT INTO #DecWdrwn
SELECT CtryId, CmId, Max(Dt) DecDt
FROM s
WHERE MaxSet > MaxDec
GROUP BY CtryId,CmId
Your response is much appreciated...
UPDATE Claims
SET CmDclnWdwnDt = (
SELECT DecWdrwnDt
FROM #DecWdrwn d
WHERE d.CmId = Claims.CmId
AND d.CtryId = Claims.CtryId
)
WHERE EXISTS (
SELECT *
FROM #DecWdrwn d
WHERE d.CmId = Claims.CmId
AND d.CtryId = Claims.CtryId
)
Please try INNER JOIN Update:
UPDATE a
SET a.CmDclnWdwnDt = b.DecWdrwnDt
FROM Claims a, #DecWdrwn b
WHERE a.CmId = b.CmId AND
a.CtryId =b.CtryId

Resources