Related
I have the following data schema:
With the following LINQ query:
var profiles = (
from p in context.BusinessProfiles
join u in context.Users on p.UserId equals u.Id
join addr in context.BusinessAddress on p.ProfileId equals addr.ProfileId into addrj
from addr in addrj.DefaultIfEmpty()
join pa in context.BusinessProfileActivities on p.ProfileId equals pa.ProfileId into paj
from paIfNull in paj.DefaultIfEmpty()
where p.ProfileId >= 137 && p.ProfileId <= 139
group new { p, u, addr, paIfNull }
by new {
p.ProfileId,
p.CompanyName,
p.Email,
UserEmail = u.Email,
addr.City, addr.Region,
addr.Country,
ActivityProfileId = paIfNull.ProfileId }
into pg
select new {
pg.Key.ProfileId,
pg.Key.CompanyName,
Email = pg.Key.Email ?? pg.Key.UserEmail,
pg.Key.City,
pg.Key.Region,
pg.Key.Country,
MatchingActivities = pg.Key.ActivityProfileId > 0 ? pg.Count() : 0
} into result
orderby result.MatchingActivities descending
select result
);
Which results with:
This result is corrent (ProfileId 137 has 0 activities, 138 has 1 and 139 has 2), but it produces the following SQL:
SELECT [b].[ProfileId], [b].[CompanyName], COALESCE([b].[Email], [a].[Email]) AS [Email], [b0].[City], [b0].[Region], [b0].[Country],
CASE WHEN [b1].[ProfileId] > CAST(0 AS bigint) THEN COUNT(*)
ELSE 0
END AS [MatchingActivities]
FROM [BusinessProfiles] AS [b]
INNER JOIN [AspNetUsers] AS [a] ON [b].[UserId] = [a].[Id]
LEFT JOIN [BusinessAddress] AS [b0] ON [b].[ProfileId] = [b0].[ProfileId]
LEFT JOIN [BusinessProfileActivities] AS [b1] ON [b].[ProfileId] = [b1].[ProfileId]
WHERE ([b].[ProfileId] >= CAST(137 AS bigint)) AND ([b].[ProfileId] <= CAST(139 AS bigint))
GROUP BY [b].[ProfileId], [b].[CompanyName], [b].[Email], [a].[Email], [b0].[City], [b0].[Region], [b0].[Country], [b1].[ProfileId]
ORDER BY CASE
WHEN [b1].[ProfileId] > CAST(0 AS bigint) THEN COUNT(*)
ELSE 0
END DESC
In SQL, I can avoid both CASE WHEN if I use COUNT([b1].[ProfileId]) like this:
SELECT [b].[ProfileId], [b].[CompanyName], COALESCE([b].[Email], [a].[Email]) AS [Email], [b0].[City], [b0].[Region], [b0].[Country],
COUNT([b1].[ProfileId]) AS [MatchingActivities]
FROM [BusinessProfiles] AS [b]
INNER JOIN [AspNetUsers] AS [a] ON [b].[UserId] = [a].[Id]
LEFT JOIN [BusinessAddress] AS [b0] ON [b].[ProfileId] = [b0].[ProfileId]
LEFT JOIN [BusinessProfileActivities] AS [b1] ON [b].[ProfileId] = [b1].[ProfileId]
WHERE ([b].[ProfileId] >= CAST(137 AS bigint)) AND ([b].[ProfileId] <= CAST(139 AS bigint))
GROUP BY [b].[ProfileId], [b].[CompanyName], [b].[Email], [a].[Email], [b0].[City], [b0].[Region], [b0].[Country], [b1].[ProfileId]
ORDER BY [MatchingActivities] DESC
My question is, how can I count by grouped ActivityProfileId = paIfNull.ProfileId using LINQ and get EF to generate the above SQL?
I have tried so many variations resulting mostly in EF to SQL errors.
MatchingActivities = pg.Count(t => t.ActivityProfileId!= 0)
MatchingActivities = pg.Select(t => t.paIfNull.ProfileId).Distinct().Count(),
MatchingActivities = pg.Count(t => t.paIfNull != null),
All result in errors like System.InvalidOperationException: The LINQ expression ... could not be translated. or getting MatchingActivities as 1 instead of 0.
Related Q/A:
LINQ Count returning 1 instead of zero for an empty group
Group by in LINQ
How to write left join, group by and average in c# entity framework Linq
In short you can't! EF Core still doesn't support that.
See this:
https://github.com/dotnet/efcore/issues/17376
And also See:
https://stackoverflow.com/a/61878332/9212040
I have added table function in the where clause.
select cmp_id, acno_code, sl_type, sl_code, 0 op_Dr, 0 op_cr, 0 tr_Dr, sum(amount) tr_Cr
from vf_finance
where cmp_id =#cmp_id1
and unitcode in (select * from UTILfn_Split( #unit_code,',') )
and stat_code in ('AT','PR' )
--and pc_code in (select * from UTILfn_Split( #sba,',') )
AND DOC_dT >=convert(datetime,#from_date,103) and doc_Dt <= convert(datetime,#to_date,103)
and amount < 0
GROUP BY cmp_id, acno_code, sl_type, sl_code
) as gl
inner join ps_Accmas acc on acc.cmp_id = gl.cmp_id and acc.acno_Code = gl.acno_code
inner join ps_owner o on gl.cmp_id = o.cmp_id
left outer join view_sl_code sl on gl.cmp_id = sl.cmp_id and gl.sl_type = sl.sl_type and gl.sl_Code = sl.sl_Code
inner join ps_slType slt on gl.cmp_id = slt.cmp_id and gl.sl_Type = slt.sl_type
where sl.sl_type in (select * from UTILfn_Split( #sl_type,',') )
and acc.acno_code in(select * from UTILfn_Split( #facno_code,',') )
group by gl.cmp_id, gl.acno_code,gl.sl_code,gl.sl_type,slt.sl_DEsc,acc.acno_DEsc, sl.sl_DEsc, o.owner_name
order by gl.cmp_id, gl.acno_code,gl.sl_code,gl.sl_type
Can anyone please suggest how I can avoid function in where clause?
You may try this. There are some issues in this existing query which I'll point first
First unitcode in (select * from UTILfn_Split( #unit_code,',') here you must use one column name instead of *, although i don't know about your function UTILfn_Split but still mention column name is preferable.
for your query you may use inner join instead of in with function having return type table.
Instead of
sl.sl_type in (select * from UTILfn_Split( #sl_type,',') )
You may try this
yourtble as sl inner join
(select value from UTILfn_Split( #sl_type,',') as t2
on sl.sl_type = t2.[value] ---- here column name with t2 depends on your function,
---what table structure is returning, in your case it is [value]
I'm running a query that contains the same subquery two times, one used in a inner join condition and the another in a outer join. I'm highlighting the repeated subquery with **. How can I optimize this in order to run only once?
SELECT DISTINCT dbo.tb_contato.Nome, dbo.tb_contato.id_contato, dbo.tb_contato.Sexo, dbo.tb_contato.codigo, dbo.tb_email.email
FROM dbo.tb_contato INNER JOIN
dbo.tb_email ON dbo.tb_contato.id_contato = dbo.tb_email.id_contato INNER JOIN
dbo.tb_empresa ON dbo.tb_empresa.id_empresa = dbo.tb_contato.id_empresa LEFT OUTER JOIN
(SELECT dbo.tb_interacao.IDContato AS id_contato
FROM dbo.tb_interacao INNER JOIN
**(SELECT MAX(IDInteracao) AS IDIntMax, IDPerfilParticipante AS id_perfil_participante, IDProjeto, IDContato
FROM dbo.tb_interacao AS tb_interacao_2
GROUP BY IDPerfilParticipante, IDProjeto, IDContato)** AS IntMax1 ON dbo.tb_interacao.IDInteracao = IntMax1.IDIntMax INNER JOIN
dbo.tb_projeto ON dbo.tb_interacao.IDProjeto = dbo.tb_projeto.id_projeto INNER JOIN
dbo.tb_status_processo ON dbo.tb_interacao.IDStatusProcesso = dbo.tb_status_processo.id_status_processo
WHERE (dbo.tb_projeto.id_projeto = 2057) AND (dbo.tb_interacao.IDPerfilParticipante = 1) AND (dbo.tb_status_processo.id_status_processo = 7) OR
(dbo.tb_projeto.id_projeto = 2057) AND (dbo.tb_interacao.IDPerfilParticipante = 1) AND (dbo.tb_status_processo.id_status_processo = 6) OR
(dbo.tb_interacao.IDPerfilParticipante = 1) AND (dbo.tb_status_processo.id_status_processo = 6) AND (dbo.tb_projeto.id_grupo = 55) OR
(dbo.tb_interacao.IDPerfilParticipante = 1) AND (dbo.tb_status_processo.id_status_processo = 7) AND (dbo.tb_projeto.id_grupo = 55))
AS ConvidadosOut ON dbo.tb_contato.id_contato = ConvidadosOut.id_contato INNER JOIN
(SELECT tb_interacao_1.IDContato AS id_contato
FROM dbo.tb_interacao AS tb_interacao_1 INNER JOIN
**(SELECT MAX(IDInteracao) AS IDIntMax, IDPerfilParticipante AS id_perfil_participante, IDProjeto, IDContato
FROM dbo.tb_interacao AS tb_interacao_3
GROUP BY IDPerfilParticipante, IDProjeto, IDContato)** AS IntMax2 ON tb_interacao_1.IDInteracao = IntMax2.IDIntMax INNER JOIN
dbo.tb_projeto AS tb_projeto_1 ON tb_interacao_1.IDProjeto = tb_projeto_1.id_projeto INNER JOIN
dbo.tb_status_processo AS tb_status_processo_1 ON tb_interacao_1.IDStatusProcesso = tb_status_processo_1.id_status_processo
WHERE (tb_projeto_1.id_projeto = 181) AND (IntMax2.id_perfil_participante = 1) AND (tb_status_processo_1.id_status_processo = 4) OR
(tb_projeto_1.id_projeto = 1581) AND (IntMax2.id_perfil_participante = 1) AND (tb_status_processo_1.id_status_processo = 5) OR
(IntMax2.id_perfil_participante = 1) AND (tb_status_processo_1.id_status_processo = 6) AND (tb_projeto_1.id_grupo = 62)) AS ConvidadosIn ON
dbo.tb_contato.id_contato = ConvidadosIn.id_contato
WHERE (dbo.tb_email.email_visibility = 0 OR
dbo.tb_email.email_visibility IS NULL) AND (dbo.tb_empresa.id_pais = 1) AND (dbo.tb_contato.Fonte <> 'salesloft_orange' AND
dbo.tb_contato.Fonte <> 'salesloft_int_orange' OR
dbo.tb_contato.Fonte IS NULL) AND (dbo.tb_contato.id_contato_visibility = 1 OR
dbo.tb_contato.id_contato_visibility IS NULL) AND (ConvidadosOut.id_contato IS NULL)
I hope you are using SQL server 2005 or later. You can safely try using Common table expressions for the purpose as stated in your question.
Below is an example of script from adventureworks database:
USE AdventureWorks2008R2;
GO
-- Define the CTE expression name and column list.
WITH Sales_CTE (SalesPersonID, SalesOrderID, SalesYear)
AS
-- Define the CTE query.
(
SELECT SalesPersonID, SalesOrderID, YEAR(OrderDate) AS SalesYear
FROM Sales.SalesOrderHeader
WHERE SalesPersonID IS NOT NULL
)
-- Define the outer query referencing the CTE name.
SELECT SalesPersonID, COUNT(SalesOrderID) AS TotalSales, SalesYear
FROM Sales_CTE
GROUP BY SalesYear, SalesPersonID
ORDER BY SalesPersonID, SalesYear;
GO
I wrote this query in Oracle and want to compile it as well on SQL Server:
SELECT DISTINCT
HOLDER_CODE, CALCULATED_AMOUNT
FROM
(SELECT
DA.HOLDER_CODE,
ROUND(FAB.AMOUNT * FCE.EXCHANGE_RATE,0) AS CALCULATED_AMOUNT,
FAB.BALANCE_DATE_ID as FAB_DATE,
MAX(FAB.BALANCE_DATE_ID) OVER (PARTITION BY DA.HOLDER_CODE) as MAX_DATE_BALANCE
FROM
DIM_ACCOUNT DA
JOIN
FACT_AS_BALANCE FAB ON FAB.ACCOUNT_ID = DA.ID
JOIN
DIM_AS_CHARACTERISTICS DAC ON DAC.ID = FAB.BALANCE_TYPE_ID
LEFT JOIN
FACT_CURRENCY_EXCHANGE FCE ON FCE.FROM_CURRENCY_ID = FAB.CURRENCY_ID
WHERE
DAC.BALANCE_CLOSING_FLAG = 'Y'
AND TO_CURRENCY_ID = (SELECT DC.ID
FROM DIM_CURRENCY DC
WHERE DC.IS_DEFAULT_CURRENCY = 'Y')
AND FAB.AMOUNT > 0)
WHERE
FAB_DATE = MAX_DATE_BALANCE
ORDER BY
CALCULATED_AMOUNT DESC;
But when I run it I get the following exception:
Error: Incorrect syntax near the keyword 'WHERE'. SQLState: S0001
ErrorCode: 156
So I guess, it's the last where clause, which indicates this. What is wrong and what should it be?
You just need to alias the subquery
SELECT DISTINCT HOLDER_CODE, CALCULATED_AMOUNT
FROM (
SELECT DA.HOLDER_CODE,
//// snip subquery
AND FAB.AMOUNT > 0
) alias_name_here //<--- here
WHERE FAB_DATE = MAX_DATE_BALANCE
ORDER BY CALCULATED_AMOUNT DESC;
You can use as alias_name_here or just alias_name_here - SQL Server allows either syntax.
you must give a name at your "table"
i give is with "as T"
SELECT DISTINCT HOLDER_CODE, CALCULATED_AMOUNT
FROM (
SELECT DA.HOLDER_CODE,
ROUND(FAB.AMOUNT * FCE.EXCHANGE_RATE,0) AS CALCULATED_AMOUNT,
FAB.BALANCE_DATE_ID as FAB_DATE,
MAX(FAB.BALANCE_DATE_ID) OVER (PARTITION BY DA.HOLDER_CODE) as MAX_DATE_BALANCE
FROM DIM_ACCOUNT DA
JOIN FACT_AS_BALANCE FAB ON FAB.ACCOUNT_ID = DA.ID
JOIN DIM_AS_CHARACTERISTICS DAC ON DAC.ID = FAB.BALANCE_TYPE_ID
LEFT JOIN FACT_CURRENCY_EXCHANGE FCE ON FCE.FROM_CURRENCY_ID = FAB.CURRENCY_ID
WHERE DAC.BALANCE_CLOSING_FLAG = 'Y'
AND TO_CURRENCY_ID = (
SELECT DC.ID FROM DIM_CURRENCY DC
WHERE DC.IS_DEFAULT_CURRENCY = 'Y')
AND FAB.AMOUNT > 0
) as T
WHERE FAB_DATE = MAX_DATE_BALANCE
ORDER BY CALCULATED_AMOUNT DESC;
SELECT
*
FROM
RM_Sales_Union
WHERE
DOCDATE >= 'September 1, 2011'
AND DOCDATE < 'October 1, 2011'
AND CUSTNMBR = '2186020'
That query results in an error:
Msg 8134, Level 16, State 1, Line 1
Divide by zero error encountered.
if run as shown.
Excluding the last line AND CUSTNMBR = '2186020' allows the query to complete.
CUSTNMBR is a char(21) field. Divide by zero has me confused.
What is the "correct" way to work around this?
RM_Sales_Union is a union query view:
SELECT ACTNUMBR_1,
ACTNUMBR_2,
ACTNUMBR_3,
ORSLSAMT,
CUSTCLAS,
CUSTNAME,
CUSTNMBR,
SLPRSNID,
DOCABREV,
CSPORNBR,
CURNCYID,
DOCDATE,
DOCNUMBR,
GLPOSTDT,
SLSAMNT,
VOIDSTTS,
SLPRSNFN,
SPRSNSLN,
DocOrigin,
ORFRTAMT,
FRTAMNT,
COMPRCNT,
TRDISAMT,
ORTDISAM,
ORMISCAMT,
ORTAXAMT,
ORCTRXAM
FROM dbo.RM_Sales_Hist
UNION
SELECT ACTNUMBR_1,
ACTNUMBR_2,
ACTNUMBR_3,
ORSLSAMT,
CUSTCLAS,
CUSTNAME,
CUSTNMBR,
SLPRSNID,
DOCABREV,
CSPORNBR,
CURNCYID,
DOCDATE,
DOCNUMBR,
GLPOSTDT,
SLSAMNT,
VOIDSTTS,
SLPRSNFN,
SPRSNSLN,
DocOrigin,
ORFRTAMT,
FRTAMNT,
COMPRCNT,
TRDISAMT,
ORTDISAM,
ORMISCAMT,
ORTAXAMT,
ORCTRXAM
FROM dbo.RM_Sales_Open
RM_Sales_Hist and RM_Sales_Open are views defined as follows:
--RM_Sales_Hist
SELECT dbo.GL_Sales_Accounts.ACTNUMBR_1,
dbo.GL_Sales_Accounts.ACTNUMBR_2,
dbo.GL_Sales_Accounts.ACTNUMBR_3,
ISNULL(dbo.MC020102.ORSLSAMT, dbo.RM30101.SLSAMNT) AS ORSLSAMT,
dbo.RM00101.CUSTCLAS,
dbo.RM00101.CUSTNAME,
dbo.RM00101.CUSTNMBR,
dbo.RM00101.SLPRSNID,
dbo.RM40401.DOCABREV,
dbo.RM30101.CSPORNBR,
dbo.RM30101.CURNCYID,
dbo.RM30101.DOCDATE,
dbo.RM30101.DOCNUMBR,
dbo.RM30101.GLPOSTDT,
dbo.RM30101.SLSAMNT,
dbo.RM30101.VOIDSTTS,
dbo.RM00301.SLPRSNFN,
dbo.RM00301.SPRSNSLN,
'HIST' AS DocOrigin,
ISNULL(dbo.MC020102.ORFRTAMT, dbo.RM30101.FRTAMNT) AS ORFRTAMT,
dbo.RM30101.FRTAMNT,
dbo.RM00301.COMPRCNT,
dbo.RM30101.TRDISAMT,
ISNULL(dbo.MC020102.ORTDISAM, 0) AS ORTDISAM,
ISNULL(dbo.MC020102.ORMISCAMT, 0) AS ORMISCAMT,
ISNULL(dbo.MC020102.ORTAXAMT, 0) AS ORTAXAMT,
ISNULL(dbo.MC020102.ORCTRXAM, 0) AS ORCTRXAM,
dbo.RM00101.STATE
FROM dbo.GL_Sales_Accounts
INNER JOIN dbo.RM30301
ON dbo.GL_Sales_Accounts.DSTINDX = dbo.RM30301.DSTINDX
INNER JOIN dbo.RM30101
ON dbo.RM30301.CUSTNMBR = dbo.RM30101.CUSTNMBR
AND dbo.RM30301.DOCNUMBR = dbo.RM30101.DOCNUMBR
INNER JOIN dbo.RM00101
ON dbo.RM30101.CUSTNMBR = dbo.RM00101.CUSTNMBR
INNER JOIN dbo.RM40401
ON dbo.RM30101.RMDTYPAL = dbo.RM40401.RMDTYPAL
INNER JOIN dbo.RM00301
ON dbo.RM00101.SLPRSNID = dbo.RM00301.SLPRSNID
LEFT OUTER JOIN dbo.MC020102
ON dbo.RM30301.DOCNUMBR = dbo.MC020102.DOCNUMBR
WHERE ( CAST(dbo.RM30301.DOCNUMBR AS VARCHAR(21)) NOT IN (SELECT
CAST(
DOCNUMBR AS
VARCHAR(21)) AS
Expr1
FROM
dbo.Invoices_With_Display_Discounts) )
--RM_Sales_Open
SELECT dbo.GL_Sales_Accounts.ACTNUMBR_1,
dbo.GL_Sales_Accounts.ACTNUMBR_2,
dbo.GL_Sales_Accounts.ACTNUMBR_3,
ISNULL(dbo.MC020102.ORSLSAMT, 0) AS ORSLSAMT,
dbo.RM00101.CUSTCLAS,
dbo.RM00101.CUSTNAME,
dbo.RM00101.CUSTNMBR,
dbo.RM00101.SLPRSNID,
dbo.RM40401.DOCABREV,
dbo.RM20101.CSPORNBR,
dbo.RM20101.CURNCYID,
dbo.RM20101.DOCDATE,
dbo.RM20101.DOCNUMBR,
dbo.RM20101.GLPOSTDT,
dbo.RM20101.SLSAMNT,
dbo.RM20101.VOIDSTTS,
dbo.RM00301.SLPRSNFN,
dbo.RM00301.SPRSNSLN,
'OPEN' AS DocOrigin,
ISNULL(dbo.MC020102.ORFRTAMT, 0) AS ORFRTAMT,
dbo.RM20101.FRTAMNT,
dbo.RM00301.COMPRCNT,
dbo.RM20101.TRDISAMT,
ISNULL(dbo.MC020102.ORTDISAM, 0) AS ORTDISAM,
ISNULL(dbo.MC020102.ORMISCAMT, 0) AS ORMISCAMT,
ISNULL(dbo.MC020102.ORTAXAMT, 0) AS ORTAXAMT,
ISNULL(dbo.MC020102.ORCTRXAM, 0) AS ORCTRXAM,
dbo.RM00101.STATE
FROM dbo.GL_Sales_Accounts
INNER JOIN dbo.RM10101
ON dbo.GL_Sales_Accounts.DSTINDX = dbo.RM10101.DSTINDX
INNER JOIN dbo.RM20101
ON dbo.RM10101.CUSTNMBR = dbo.RM20101.CUSTNMBR
AND dbo.RM10101.DOCNUMBR = dbo.RM20101.DOCNUMBR
INNER JOIN dbo.RM00101
ON dbo.RM20101.CUSTNMBR = dbo.RM00101.CUSTNMBR
INNER JOIN dbo.RM40401
ON dbo.RM20101.RMDTYPAL = dbo.RM40401.RMDTYPAL
INNER JOIN dbo.RM00301
ON dbo.RM00101.SLPRSNID = dbo.RM00301.SLPRSNID
LEFT OUTER JOIN dbo.MC020102
ON dbo.RM10101.DOCNUMBR = dbo.MC020102.DOCNUMBR
WHERE ( CAST(dbo.RM20101.DOCNUMBR AS VARCHAR(21)) NOT IN (SELECT
CAST(
DOCNUMBR AS
varchar(21)) AS
Expr1
FROM
dbo.Invoices_With_Display_Discounts) )
I guess that RM_Sales_Union is a view that contains a division. The filter simply changes something like a COUNT or SUM to give zero, and it is this that is used as divisor
Change it to use something/NULLIF(..., 0) to make the expressions give NULL. Or ISNULL(something/NULLIF(..., 0), 0) to get NULL instead