I have the following stored procedure. I implemented the UNION to avoid writing 2 stored procedures. My main issue is that EF will not recognize the dynamically created column, even if I add it manually to the object_result class that queries the model. Does anyone know how to get such dynamically generated columns to work?
(What's weird is the RANK() VolumeRank below does work. But RefYear does not map).
SQL:
BEGIN
SET NOCOUNT ON;
SET FMTONLY OFF;
SELECT *
FROM
(SELECT
B.OS, B.DS, COUNT(DISTINCT A.PO) Volume,
RANK() OVER (ORDER BY COUNT(DISTINCT A.PO) DESC) VolumeRank,
YEAR(GETDATE()) RefYear
FROM
DB.DW.LoadData AS A
JOIN
DB.DW.Geo AS B ON A.PO = B.PO
INNER JOIN
DW.DMaster AS C ON A.LoadDateKey = C.DateKey
WHERE
C.IsT12Weeks_LastWeek = 1
AND a.PrimaryCustomerID = #CustomerId
GROUP BY
B.OS, B.DS) AS A
WHERE
VolumeRank <= 3
UNION
SELECT *
FROM
(SELECT
B.OS, B.DS,
COUNT(DISTINCT A.PO) Volume,
RANK() OVER (ORDER BY COUNT(DISTINCT A.PO) DESC) VolumeRank,
YEAR(getdate())-1 RefYear
FROM
DB.DW.LoadData AS A
JOIN
DB.DW.Geo AS B ON A.PO = B.PO
INNER JOIN
DW.DMaster AS C ON A.LoadDateKey = C.DateKey
WHERE
PrimaryCustomerID = #CustomerId
AND FWeek >= #FirstWeek
AND FWeek <= #LastWeek
AND FiscalYear = #FiscalYear
GROUP BY
B.OS, B.DS) AS A
WHERE
VolumeRank <= 3
ORDER BY
RefYear DESC, Volume DESC
END
C#:
List<spGetT_Result> result = new List<spGetT_Result>();
using (var db = new T_Entities())
{
result = db.spGetT(eid, firstWeek, lastWeek, year).ToList();
}
Model class includes this line, but it's always null when I run:
public Nullable<int> RefYear { get; set; }
Related
I have some tables. I just want to show it in a pay chart as a percentage. How many rows have a table? I try to count all rows and store them in a variable. I have a DTO model class. That class contains All the properties same Stored procedure variables. The results are exactly the same as our expected results. But when I try to invoke the stored procedure then shows an error. But when I Pass a single variable then it works well.
ALTER PROCEDURE [dbo].[GetTotalNumberOfFormsSubmitted]
AS
BEGIN
(select BurnOut = count(*) from Client_BurnOuts
UNION ALL
select CIP = count(*) from Client_CIPEnergyCrisises
UNION ALL
select HomeRepair = count(*) from Client_HomeRepairs
UNION ALL
select MadicalTravelAssistan = count(*) from Client_MedicalTravelAssistances
UNION ALL
select EmergencyAssistance = count(*) from Client_EmergencyAssistances
UNION ALL
select Heating = count(*) from Client_Heatings
UNION ALL
select DuckFoundation = count(*) from Client_DukeEnergyFoundations
UNION ALL
select FoodPantry = count(*) from Client_EmergencyFoodPantries
UNION ALL
select MedicalEquipment = count(*) from Client_MedicalEquipments)
END
finally solved my problem, that is my solution
ALTER PROCEDURE [dbo].[GetTotalNumberOfFormsSubmitted]
AS
BEGIN
select
br.BurnOut, ho.HomeRepair, cip.CIPEnergyCrisises, mta.MadicalTravelAssistan, ea.EmergencyAssistance, he.Heating
from
(select BurnOut = count(*) from Client_BurnOuts) as br,
(select HomeRepair = count(*) from Client_HomeRepairs) as ho,
(select CIPEnergyCrisises = count(*) from Client_CIPEnergyCrisises) as cip,
(select MadicalTravelAssistan = count(*) from Client_MedicalTravelAssistances) as mta,
(select EmergencyAssistance = count(*) from Client_EmergencyAssistances) ea,
(select Heating = count(*) from Client_Heatings) as he,
(select DuckFoundation = count(*) from Client_Heatings) as du,
(select FoodPantry = count(*) from Client_Heatings) as fp,
(select MedicalEquipment = count(*) from Client_Heatings) as me
END
I have a query which is working great when I used it in SQL Server Management Studio. It is worth to mention here that in PIVOT [MedicalInformation.MedicalInformationForm.Name.firstName] and [MedicalInformation.MedicalInformationForm.Name.lastName] are NOT static and can vary a.k.a they can be change during run time with different columns
WITH pivot_data AS
(
SELECT
[p].RecordId, -- Grouping Column
[Key], -- Spreading Column
[Value] -- Aggregate Column
FROM
[RecordDatas] AS [p]
INNER JOIN
(SELECT [p0].*
FROM [Records] AS [p0]
WHERE [p0].[IsDeleted] = 0) AS [t] ON [p].[RecordId] = [t].[ID]
WHERE
[p].DatasetId = 1386
AND [p].[IsDeleted] = 0
AND ([t].[ProjectID] = 191)
AND [Key] IN ('MedicalInformation.MedicalInformationForm.Name.firstName',
'MedicalInformation.MedicalInformationForm.Name.lastName')
)
SELECT
RecordId,
[MedicalInformation.MedicalInformationForm.Name.firstName],
[MedicalInformation.MedicalInformationForm.Name.lastName]
FROM
pivot_data
PIVOT
(MAX([Value])
FOR [Key] IN ([MedicalInformation.MedicalInformationForm.Name.firstName],
[MedicalInformation.MedicalInformationForm.Name.lastName])) AS p
The problem occurs when I'm trying to build the same query dynamic from C#:
var sql1 = #"
WITH pivot_data AS
(
SELECT [p].RecordId, -- Grouping Column
[Key], -- Spreading Column
[Value] -- Aggregate Column
FROM [RecordDatas] AS [p]
INNER JOIN (
SELECT [p0].*
FROM [Records] AS [p0]
WHERE [p0].[IsDeleted] = 0
) AS [t] ON [p].[RecordId] = [t].[ID]
where [p].DatasetId = 1386
AND [p].[IsDeleted] = 0
AND ([t].[ProjectID] = 191)
AND [Key] IN( {0} , {1}))
SELECT RecordId, {2},{3}
FROM pivot_data
PIVOT (max([Value]) FOR [Key] IN ({2},{3})) AS p;";
await repositoryMapper.Repository.Context.Database.ExecuteSqlCommandAsync(sql1,
"MedicalInformation.MedicalInformationForm.Name.firstName",
"MedicalInformation.MedicalInformationForm.Name.lastName",
"[MedicalInformation.MedicalInformationForm.Name.firstName]",
"[MedicalInformation.MedicalInformationForm.Name.lastName]");
And here is coming my problem - when query is generated with ExecuteSqlCommandAsync and adding parameters for the PIVOT columns ([MedicalInformation.MedicalInformationForm.Name.firstName] and [MedicalInformation.MedicalInformationForm.Name.lastName]) there is something wrong with syntax. I'm getting an error:
Incorrect syntax near '#p2'.
I have tried to get the generated query which Entity Framework is doing and it looks like this :
exec sp_executesql N'
WITH pivot_data AS
(
SELECT [p].RecordId, -- Grouping Column
[Key], -- Spreading Column
[Value] -- Aggregate Column
FROM [RecordDatas] AS [p]
INNER JOIN (
SELECT [p0].*
FROM [Records] AS [p0]
WHERE [p0].[IsDeleted] = 0
) AS [t] ON [p].[RecordId] = [t].[ID]
where [p].DatasetId = 1386
AND [p].[IsDeleted] = 0
AND ([t].[ProjectID] = 191)
AND [Key] IN( #p0 , #p1))
SELECT RecordId, #p2,#p3
FROM pivot_data
PIVOT (max([Value]) FOR [Key] IN (#p2,#p3)) AS p;
',N'#p0 nvarchar(4000),
#p1 nvarchar(4000),
#p2 nvarchar(4000),
#p3 nvarchar(4000)',
#p0=N'MedicalInformation.MedicalInformationForm.Name.firstName',
#p1=N'MedicalInfo rmation.MedicalInformationForm.Name.lastName',
#p2=N'[MedicalInformation.MedicalInformationForm.Name.firstName]',
#p3=N'[MedicalInformation.MedicalInformationForm.Name.lastName]'
Please help me I'm not sure what is the problem here
The problem is that a line like this:
SELECT RecordId, #p2,#p3
is not valid. Statements are precompiled and you can not change field names in parameters - field names for a statement are static.
You have to go into dynamic SQL, which EntityFramework does not support (outside of the functions allowing you to submit whatever SQL you want). Basically you must create the valid statement in a way that is NOT using parameters are field names.
This does NOT mean you can not use placeholders and - in C# - run a replace operation on the string, but it means that the replacement has to be finished when you THEN submit the changed script to the database.
Limitation of SQL Server.
You can not assign value of these #p2,#p3 in SELECT RecordId, #p2,#p3 from parameter. You need to set these statement before running query as follow:
var sql1 = #"
WITH pivot_data AS
(
SELECT [p].RecordId, -- Grouping Column
[Key], -- Spreading Column
[Value] -- Aggregate Column
FROM [RecordDatas] AS [p]
INNER JOIN (
SELECT [p0].*
FROM [Records] AS [p0]
WHERE [p0].[IsDeleted] = 0
) AS [t] ON [p].[RecordId] = [t].[ID]
where [p].DatasetId = 1386
AND [p].[IsDeleted] = 0
AND ([t].[ProjectID] = 191)
AND [Key] IN( {0} , {1}))
SELECT RecordId, [MedicalInformation.MedicalInformationForm.Name.lastName],[MedicalInformation.MedicalInformationForm.Name.lastName]
FROM pivot_data
PIVOT (max([Value]) FOR [Key] IN ({2},{3})) AS p;";
await repositoryMapper.Repository.Context.Database.ExecuteSqlCommandAsync(sql1,
"MedicalInformation.MedicalInformationForm.Name.firstName",
"MedicalInformation.MedicalInformationForm.Name.lastName",
"[MedicalInformation.MedicalInformationForm.Name.firstName]",
"[MedicalInformation.MedicalInformationForm.Name.lastName]");
I am using Entity framework for executing code for left join and the sql code that is being generated has mapping issue for one parameter
This mapping issue is when i am using IQuerable object on end of the join and the other end is context object
If I use context object instead of Iquerable then the sql code generated is proper.
E.g. DB entity name: order
Property name: OrderId
Mapped to Table Name : Orders & column order_id
If in my Iquerable object if i alias OrderId as order_id that resolves the issue as both my alias and my db column name is same so there is no mapping issue.
If in my Iquerable object if i alias OrderId as order_id that resolves the issue as both my alias and my db column name is same so there is no mapping issue.
var query =(from o in context.Orders
where o.BranchKey == branchKey
Select new
{
o.OrderId,
o.BranchKey
}
);
query = query.Skip(firstRecord).Take(count).AsQueryable();
var orderDetails = (from o in query
join od in context.OrderDetails
on new {o.orderId , o.BranchKey } equals new { od.OrderId, od.BranchKey } into ordergroup
from grp in ordergroup.DefaultIfEmpty()
select new
{
o.order_id,
o.BranchKey
}
)
**Expected Result**
exec sp_executesql N'SELECT [t].[OrderId], [t].[BranchKey]
FROM (SELECT [o].[order_id] AS [OrderId], [o].[BranchKey]
FROM [dbo].[Orders] AS [o]
WHERE ((([o].[BranchKey] = #__branchKey_0)
OFFSET #__p_4 ROWS FETCH NEXT #__p_5 ROWS ONLY
) AS [t]
LEFT JOIN [dbo].[order_details] AS **[od] ON ([t].[orderId] = [od].[order_id])** AND ([t].[BranchKey] = [od].[BranchKey])
**Actual Result**
exec sp_executesql N'SELECT [t].[OrderId], [t].[BranchKey]
FROM (
SELECT [o].[order_id] AS [OrderId], [o].[BranchKey]
FROM [dbo].[Orders] AS [o]
WHERE ((([o].[BranchKey] = #__branchKey_0)
OFFSET #__p_4 ROWS FETCH NEXT #__p_5 ROWS ONLY
) AS [t]
LEFT JOIN [dbo].[order_details] AS **[od] ON ([t].[order_id] = [od].[order_id])** AND ([t].[BranchKey] = [od].[BranchKey])
I tried to set a value to variable in sub query but it doesn't work.
Here is my query:
declare #val1 int
declare #val2 int
select #val1 = sum(column1)
,(select #val2 = (select sum(column2) from table2))
,(#val1+#val2)Result
from table 1
What I want to do is setting #val2 for sub query help me please
I meant set in Sub query not separate select statement
Just use 3 separate selects:
select #val1 = sum(column1) from table1
select #val2 = sum(column2) from table2
select (#val1+#val2) as Result
Or you can also write 2 selects:
select #val1 = sum(column1),
#val2 = (select SUM(column2) from table2)
from table1
select (#val1 + #val2) Result
But not just 1 select:
A SELECT statement that assigns a value to a variable must not be
combined with data-retrieval operations
If you need to accomplish all in one select and return a recordset, do not use variables, do it like this:
SELECT sum1 + sum2 FROM (
select sum(column1) as sum1,
(select SUM(column2) from table2) as sum2
from table1
) subquery
I'm newish to LinqToSQL and the project that I am working on cannot be changed to something else. I am translating some old SQL code to Linq. Not being that hot at linq, I used Linqer to do the translation for me. The query took about 90 seconds to run, so I thought it must be the linqToSQL. However, when I copied the query that the LinqToSQL produced and ran an ExecuteQuery on the datacontext it was super quick as I expected. I've copied the full queries, rather than trying to distil it down, but it looks like the issue is with something LinqToSQL is doing behind the scenes.
To summarise, if I copy the T-SQL created by linq and run
var results = DB.ExecuteQuery<InvoiceBalanceCheckDTO.InvoiceBalanceCheck>(#"T-SQL created by Linq - see below").ToList()
it completes with expected results in about 0.5 seconds.
It runs about the same time directly in SSMS. However, if I use the linqToSQL code that creates the T-SQL and do ToList() it takes ages. The result is only 9 records, although without the constraint to check the balance <> 0, there would be around 19,000 records. It's as if it's getting all 19,000 and then checking <> 0 after it's got the records.
I have also changed the Linq to project into the class used above, rather than to an anonymous type, but it makes not difference
This is the original SQL :
SELECT InvoiceNum, Max(AccountCode), Sum(AmountInc) AS Balance
FROM
(SELECT InvoiceNum, AccountCode, AmountInc From TourBookAccount WHERE AccDetailTypeE IN(20,30) AND InvoiceNum >= 1000
UNION ALL
SELECT InvoiceNum, '<no matching invoice>' AS AccountCode, AccountInvoiceDetail.AmountInc
FROM AccountInvoiceDetail
INNER JOIN AccountInvoice ON AccountInvoiceDetail.InvoiceID=AccountInvoice.InvoiceID
WHERE AccDetailTypeE IN(20,30)
AND InvoiceNum >= 1000
) as t
GROUP BY InvoiceNum
HAVING (Sum(t.AmountInc)<>0)
ORDER BY InvoiceNum
and this is the linq
var test = (from t in
(
//this gets the TourBookAccount totals
from tba in DB.TourBookAccount
where
detailTypes.Contains(tba.AccDetailTypeE) &&
tba.InvoiceNum >= dto.CheckInvoiceNumFrom
select new
{
InvoiceNum = tba.InvoiceNum,
AccountCode = tba.AccountCode,
Balance = tba.AmountInc
}
)
.Concat //note that concat, since it's possible that the AccountInvoice record does not actually exist
(
//this gets the Invoice detail totals.
from aid in DB.AccountInvoiceDetail
where
detailTypes.Contains(aid.AccDetailTypeE) &&
aid.AccountInvoice.InvoiceNum >= dto.CheckInvoiceNumFrom &&
select new
{
InvoiceNum = aid.AccountInvoice.InvoiceNum,
AccountCode = "<No Account Records>",
Balance = aid.AmountInc
}
)
group t by t.InvoiceNum into g
where Convert.ToDecimal(g.Sum(p => p.Balance)) != 0m
select new
{
InvoiceNum = g.Key,
AccountCode = g.Max(p => p.AccountCode),
Balance = g.Sum(p => p.Balance)
}).ToList();
and this is the T-SQL that the linq produces
SELECT [t5].[InvoiceNum], [t5].[value2] AS [AccountCode], [t5].[value3] AS [Balance]
FROM (
SELECT SUM([t4].[AmountInc]) AS [value], MAX([t4].[AccountCode]) AS [value2], SUM([t4].[AmountInc]) AS [value3], [t4].[InvoiceNum]
FROM (
SELECT [t3].[InvoiceNum], [t3].[AccountCode], [t3].[AmountInc]
FROM (
SELECT [t0].[InvoiceNum], [t0].[AccountCode], [t0].[AmountInc]
FROM [dbo].[TourBookAccount] AS [t0]
WHERE ([t0].[AccDetailTypeE] IN (20, 30)) AND ([t0].[InvoiceNum] >= 1000)
UNION ALL
SELECT [t2].[InvoiceNum],'<No Account Records>' AS [value], [t1].[AmountInc]
FROM [dbo].[AccountInvoiceDetail] AS [t1]
INNER JOIN [dbo].[AccountInvoice] AS [t2] ON [t2].[InvoiceID] = [t1].[InvoiceID]
WHERE ([t1].[AccDetailTypeE] IN (20, 30)) AND ([t2].[InvoiceNum] >= 1000)
) AS [t3]
) AS [t4]
GROUP BY [t4].[InvoiceNum]
) AS [t5]
WHERE [t5].[value] <> 0
I would bet money, that the problem is in this line:
where Convert.ToDecimal(g.Sum(p => p.Balance)) != 0m
What is probably happening, is that it can't translate this to SQL and silently tries to get all rows from db to memory, and then do filtering on in memory objects (LINQ to objects)
Maybe try to change this to something like:
where g.Sum(p=>.Balance!=0)
Well, the answer turned out not to be LinqToSQL itself (although possibly the way it creates the query could be blamed) , but the way SQL server handles the query. When I was running the query on the database to check speed (and running the created T=SQL in DB.ExecuteQuery) I had all the variables hardcoded. When I changed it to use the exact sql that Linq produces (i.e. with variables that are substituted) it ran just as slow in SSMS.
Looking at the execution plans of the two, they are quite different. A quick search on SO brought me to this page : Why does a parameterized query produces vastly slower query plan vs non-parameterized query which indicated that the problem was SQL server's "Parameter sniffing".
The culprit turned out to be the "No Account Records" string
For completeness, here is the generated T-SQL that Linq creates.
Change #p10 to the actual hardcoded string, and it's back to full speed !
In the end I just removed the line from the linq and set the account code afterwards and all was good.
Thanks #Botis,#Blorgbeard,#ElectricLlama & #Scott for suggestions.
DECLARE #p0 as Int = 20
DECLARE #p1 as Int = 30
DECLARE #p2 as Int = 1000
DECLARE #p3 as Int = 20
DECLARE #p4 as Int = 30
DECLARE #p5 as Int = 1000
DECLARE #p6 as Int = 40
DECLARE #p7 as Int = 10
DECLARE #p8 as Int = 0
DECLARE #p9 as Int = 1
DECLARE #p10 as NVarChar(4000)= '<No Account Records>' /*replace this parameter with the actual text in the SQl and it's way faster.*/
DECLARE #p11 as Decimal(33,4) = 0
SELECT [t5].[InvoiceNum], [t5].[value2] AS [AccountCode], [t5].[value3] AS [Balance]
FROM (
SELECT SUM([t4].[AmountInc]) AS [value], MAX([t4].[AccountCode]) AS [value2], SUM([t4].[AmountInc]) AS [value3], [t4].[InvoiceNum]
FROM (
SELECT [t3].[InvoiceNum], [t3].[AccountCode], [t3].[AmountInc]
FROM (
SELECT [t0].[InvoiceNum], [t0].[AccountCode], [t0].[AmountInc]
FROM [dbo].[TourBookAccount] AS [t0]
WHERE ([t0].[AccDetailTypeE] IN (#p0, #p1)) AND ([t0].[InvoiceNum] >= #p2)
UNION ALL
SELECT [t2].[InvoiceNum], #p10 AS [value], [t1].[AmountInc]
FROM [dbo].[AccountInvoiceDetail] AS [t1]
INNER JOIN [dbo].[AccountInvoice] AS [t2] ON [t2].[InvoiceID] = [t1].[InvoiceID]
WHERE ([t1].[AccDetailTypeE] IN (#p3, #p4)) AND ([t2].[InvoiceNum] >= #p5) AND ([t2].[InvoiceStatusE] <= #p6) AND ([t2].[InvoiceTypeE] = #p7) AND ([t1].[BookNum] <> #p8) AND ([t1].[AccDetailSourceE] = #p9)
) AS [t3]
) AS [t4]
GROUP BY [t4].[InvoiceNum]
) AS [t5]
WHERE [t5].[value] <> #p11
SELECT [t5].[InvoiceNum], [t5].[value2] AS [AccountCode], [t5].[value3] AS [Balance]
FROM (
SELECT SUM([t4].[AmountInc]) AS [value], MAX([t4].[AccountCode]) AS [value2], SUM([t4].[AmountInc]) AS [value3], [t4].[InvoiceNum]
FROM (
SELECT [t3].[InvoiceNum], [t3].[AccountCode], [t3].[AmountInc]
FROM (
SELECT [t0].[InvoiceNum], [t0].[AccountCode], [t0].[AmountInc]
FROM [dbo].[TourBookAccount] AS [t0]
WHERE ([t0].[AccDetailTypeE] IN (20, 30)) AND ([t0].[InvoiceNum] >= 1000)
UNION ALL
SELECT [t2].[InvoiceNum], '<No Account Records>' AS [value], [t1].[AmountInc]
FROM [dbo].[AccountInvoiceDetail] AS [t1]
INNER JOIN [dbo].[AccountInvoice] AS [t2] ON [t2].[InvoiceID] = [t1].[InvoiceID]
WHERE ([t1].[AccDetailTypeE] IN (20, 30)) AND ([t2].[InvoiceNum] >= 0) AND ([t2].[InvoiceStatusE] <= 40) AND ([t2].[InvoiceTypeE] = 10) AND ([t1].[BookNum] <> 0) AND ([t1].[AccDetailSourceE] = 1)
) AS [t3]
) AS [t4]
GROUP BY [t4].[InvoiceNum]
) AS [t5]
WHERE [t5].[value] <> 0