在LINQ中转换OrderBy Case语句

时间:2015-12-01 18:48:30

标签: c# entity-framework linq

我试图将以下SQL从Oracle转换为Linq to Entity查询。

ORDER by
    case when(e.prev_co = 'ABC' and(nvl(co_seniority, '1-jan-2099') < to_date('10-apr-2001'))) 
            then '2001-04-01'
         else to_char(nvl(co_seniority, '1-jan-2099'), 'YYYY-MM-DD') end,
    nvl(co_seniority, '1-jan-2099'), 
    birth_dt

我希望我可以使用函数传递一些参数并让它返回正确的日期。我尝试创建一个名为SortDate的新属性,然后在我的页面上创建一个函数,该函数将接收参数并返回正确的日期,但这不起作用。我得到并且异常说&#34; LINQ to Entities无法识别方法GetSortDate&#34;。

Model
SortByDate = GetSortDate(e.PREV_CO, e.CO_SENIORITY),

Function

public static DateTime GetSortDate(string PreviousCo, DateTime? CoSeniorityDate)
{
    //set variable to default date
    DateTime sortDate = System.DateTime.Parse("2001-04-01");

    //set default date if NULL
    if (CoSeniorityDate == null)
    {
        CoSeniorityDate = System.DateTime.Parse("2099-01-01");
    }

    if (PreviousCo == "ABC" && (CoSeniorityDate < System.DateTime.Parse("2001-04-10")))
    {
        sortDate = System.DateTime.Parse("2001-04-01");
    }
    else
    {
        sortDate = System.DateTime.Parse(CoSeniorityDate.ToString());
    }

    return sortDate;
}

这是我完整的EF

using (DataContext db = new DataContext())
{
    db.Configuration.AutoDetectChangesEnabled = false;      //no changes needed so turn off for performance.

    var workStatus = new string[] { "1", "3" };
    var company = new string[] { "EX", "SM" };
    var eventReason = new string[] { "21", "22", "23" };

    data = (from e in db.EMPLOYEE
               where workStatus.Contains(e.WORKSTAT)
                   && company.Contains(e.CO.Substring(0, 2))
                   && ((e.EVENT_TYP != "35") || (e.EVENT_TYP == "35" && !eventReason.Contains(e.EVENT_RSN)))
            select new Employee
            {
                Co = e.CO,
                CityCode = e.CITY_CODE,
                EmployeeNumber = e.EMP,
                LastName = e.LAST_NAME,
                FirstName = e.FIRST_NAME,
                Position = e.ABV_POSITION_TITLE,
                EmploymentType = e.PART_TIME_IND == "X" ? "PT" : "FT",
                SeniorityDate = e.CO_SENIORITY == null ? DateTime.MaxValue : e.CO_SENIORITY,
                BirthDate = e.BIRTH_DT,
                SortByDate = GetSortDate(e.PREV_CO, e.CO_SENIORITY),
                PreviousCo = e.PREV_CO
            }).OrderBy(o => o.SortByDate).ThenBy(o => o.SeniorityDate).ThenBy(o => o.BirthDate).ToList();
}

任何人都有关于如何转换此订单的建议吗?

更新的问题

目前,通过使用像@Markus这样的辅助SELECT,我的查询工作正常。第一个查询只是拉取数据,然后是所有格式化和调用方法以获取正确的SortByDate。

但是,我的经理真的更喜欢在数据库和内存中进行排序。他让这一个去了,因为很少有人称这个资历名单,而且每月只有一次。

出于学习目的,我想看看我是否可以让DB进行所有排序,如下所示@IvanStoev。所以,回到那条路线,我无法让OrderBy完全按照它应该的方式工作。

如果你查看原始SQL我试图转换它首先查看该人是否有以前的公司“ABC”,如果他们这样做,然后查看SeniorityDate(设置一个默认日期,如果那是NULL)并将其与收购日期进行比较。如果不满足该条件,则只需使用他们的SeniorityDate(如果为NULL则设置为默认值)。整蛊......我知道。

使用LinqPad中建议的OrderBy,然后查看返回的SQL,我可以看到OrderBy的第一部分查看上一个公司,然后查看SeniorityDate并设置一个值。然后它查看收购日期。我需要以某种方式对某些条件进行分组以便首先查看哪些条件我不知道它是否可能。

SELECT t0.ABV_POSITION_TITLE, t0.BIRTH_DT, t0.CITY_CODE, t0.CO, t0.CO_SENIORITY, t0.EMP, t0.FIRST_NAME, t0.LAST_NAME, t0.PART_TIME_IND, t0.PREV_CO, t0.WORKSTAT
FROM SAP_EMPLOYEE t0
WHERE ((((t0.WORKSTAT IN (:p0, :p1) AND (t0.PERS_SUB_AREA = :p2)) AND SUBSTR(t0.CO, 0 + 1, 2) IN (:p3, :p4)) AND (t0.CO <> :p5)) AND ((t0.EVENT_TYP <> :p6) OR ((t0.EVENT_TYP = :p6) AND NOT t0.EVENT_RSN IN (:p7, :p8, :p9))))
ORDER BY (CASE WHEN ((t0.PREV_CO = :p10) AND (t0.CO_SENIORITY IS NULL)) THEN :p11 WHEN (t0.CO_SENIORITY < :p12) THEN :p13 ELSE COALESCE(t0.CO_SENIORITY, :p11) END), COALESCE(t0.CO_SENIORITY, :p11), t0.BIRTH_DT
-- p0 = [1]
-- p1 = [3]
-- p2 = [200A]
-- p3 = [EX]
-- p4 = [SM]
-- p5 = [EXGS]
-- p6 = [35]
-- p7 = [21]
-- p8 = [22]
-- p9 = [23]
-- p10 = [ABC]
-- p11 = [1/1/2099 12:00:00 AM]
-- p12 = [4/10/2001 12:00:00 AM]
-- p13 = [4/1/2001 12:00:00 AM]

我需要提出像

这样的东西
ORDER BY (CASE WHEN ((t0.PREV_CO = :p10) AND (COALESCE(t0.CO_SENIORITY, :p11) < :p12) THEN :p13 ELSE COALESCE(t0.CO_SENIORITY, :p11) END)

以下是我在LinqPad中使用的代码。

void Main()
{
    var workStatus = new string[] { "1", "3" };
    var company = new string[] { "EX", "SM" };
    var eventReason = new string[] { "21", "22", "23" };

    var baseDate = new DateTime(2001, 4, 10);  // 10-apr-2001
    var minDate = new DateTime(2001, 4, 1);    // 1-apr-2001
    var abcDate = new DateTime(2001, 4, 10);   // 10-apr-2001
    var maxDate = new DateTime(2099, 1, 1);    // 1-jan-2099

    var data = (from e in SAP_EMPLOYEE
                where workStatus.Contains(e.WORKSTAT)
                        && e.PERS_SUB_AREA == "200A"
                        && company.Contains(e.CO.Substring(0, 2))
                        && e.CO != "EXGS"
                        && ((e.EVENT_TYP != "35") || (e.EVENT_TYP == "35" && !eventReason.Contains(e.EVENT_RSN)))
                orderby e.PREV_CO == "ABC" && e.CO_SENIORITY == null ? maxDate : e.CO_SENIORITY < abcDate ? minDate : e.CO_SENIORITY ?? maxDate,
                e.CO_SENIORITY ?? maxDate,
                e.BIRTH_DT
                    select new Employee
                    {
                        Co = e.CO,
                        CityCode = e.CITY_CODE,
                        EmployeeNumber = e.EMP,
                        LastName = e.LAST_NAME,
                        FirstName = e.FIRST_NAME,
                        Position = e.ABV_POSITION_TITLE,
                        EmploymentType = e.PART_TIME_IND == "X" ? "PT" : "FT",
                        SeniorityDate = e.CO_SENIORITY == null ? maxDate :
                            e.PREV_CO == "ABC" && e.CO_SENIORITY < twaDate ? maxDate : e.CO_SENIORITY,
                        LOA = e.WORKSTAT == "1" ? "LOA" : "",
                        ABC = e.PREV_CO == "ABC" ? "ABC" : "",
                        BirthDate = e.BIRTH_DT,
                        PreviousCo = e.PREV_CO
                    }).ToList();

    data.Dump();
}

2 个答案:

答案 0 :(得分:1)

异常的原因是实体框架在执行时会生成SQL查询。在您的情况下,这会在最后调用from mpl_toolkits.mplot3d import Axes3D import matplotlib.pyplot as plt import numpy as np import os path = '/users/unsername/Desktop/untitled folder/python files/MSII_phasespace/' os.chdir( path ) fig = plt.figure() ax = fig.add_subplot(111, projection='3d') #Data data = np.load('msii_phasespace.npy',mmap_mode='r') # data.size: 167197 # data.shape: (167197,) # data.dtype: dtype([('x', '<f4'), ('y', '<f4'), ('z', '<f4'), # ('velx', '<f4'), ('vely', '<f4'), ('velz', '<f4'), ('m200', '<f4')]) u = data['x'] v = data['y'] w = data['z'] Xs = np.arange(0,500, 1000) Ys = np.arange(0,500, 1000) Xs, Ys = np.meshgrid(Xs, Ys) Zs = np.arange(0,500, 1000) ax.plot(u,v,w) ax.plot_wireframe(Xs,Ys,Zs) plt.show() 时发生。为了生成SQL查询,实体框架分析查询并将其转换为SQL。由于实体框架不知道您的函数,因此无法为其生成SQL语句。

为了解决这个问题,您需要先执行查询并在结果中对内存进行排序操作。为了限制传输到客户端的数据量,您应该执行包含ToList()子句的查询,并告诉EF您感兴趣的哪些字段,以避免包含所有字段的where的表。

您可以按如下方式更改查询:

SELECT * FROM ...

此查询首先过滤data = (from e in db.EMPLOYEE where workStatus.Contains(e.WORKSTAT) && company.Contains(e.CO.Substring(0, 2)) && ((e.EVENT_TYP != "35") || (e.EVENT_TYP == "35" && !eventReason.Contains(e.EVENT_RSN))) select new () { Co = e.CO, CityCode = e.CITY_CODE, EmployeeNumber = e.EMP, LastName = e.LAST_NAME, FirstName = e.FIRST_NAME, Position = e.ABV_POSITION_TITLE, EmploymentType = e.PART_TIME_IND == "X" ? "PT" : "FT", SeniorityDate = e.CO_SENIORITY, BirthDate = e.BIRTH_DT, PreviousCo = e.PREV_CO }).ToList().Select(x => new Employee() { Co = x.Co, CityCode = x.CityCode, EmployeeNumber = x.EmployeeNumber, LastName = x.LastName, FirstName = x.FirstName, Position = x.Position, EmploymentType = x.EmploymentType, SeniorityDate = x.SeniorityDate ?? DateTime.MaxValue, BirthDate = x.BirthDate, SortByDate = GetSortDate(x.PreviousCo, x.SeniorityDate), PreviousCo = x.PreviousCo }).OrderBy(o => o.SortByDate) .ThenBy(o => o.SeniorityDate) .ThenBy(o => o.BirthDate).ToList(); 子句中指定的数据,然后使用匿名类型仅检索相关字段 - 包括稍后用作where方法输入的字段具有原始价值。在第一个GetSortDate结果出现在内存中之后,您可以先添加一个新的选择来创建ToList对象,包括排序日期。然后按排序日期对这些对象进行排序,依此类推。

Employee方法的一个小提示:将DateTime常量指定为解析的字符串不是一个好主意,因为解析依赖于线程的文化(如果没有指定文化)。

GetSortDate

答案 1 :(得分:1)

正如您已经注意到的(困难的方法),在LINQ to Entities查询中,您无法使用LINQ to Objects中的本地方法。如果希望在数据库中执行整个查询,则需要仅使用受支持的构造将逻辑嵌入查询中。

话虽如此,相当于你的SQL查询应该是这样的

var baseDate = new DateTime(2001, 4, 10); // 10-apr-2001
var minDate = new DateTime(2001, 4, 1);   // 1-apr-2001
var maxDate = new DateTime(2099, 1, 1);   // 1-jan-2099

data = (from e in db.EMPLOYEE
        where workStatus.Contains(e.WORKSTAT)
            && company.Contains(e.CO.Substring(0, 2))
            && ((e.EVENT_TYP != "35") || (e.EVENT_TYP == "35" && !eventReason.Contains(e.EVENT_RSN)))
        let seniorityDate = e.CO_SENIORITY ?? maxDate
        let sortDate = 
            e.CO_SENIORITY == null ? maxDate : 
            e.PREV_CO == "ABC" && e.CO_SENIORITY < baseDate ? minDate :
            e.CO_SENIORITY
        orderby sortDate, seniorityDate, e.BIRTH_DT
        select new Employee
        {
            Co = e.CO,
            CityCode = e.CITY_CODE,
            EmployeeNumber = e.EMP,
            LastName = e.LAST_NAME,
            FirstName = e.FIRST_NAME,
            Position = e.ABV_POSITION_TITLE,
            EmploymentType = e.PART_TIME_IND == "X" ? "PT" : "FT",
            SeniorityDate = e.CO_SENIORITY,
            BirthDate = e.BIRTH_DT,
            PreviousCo = e.PREV_CO
        }).ToList();

更新:出于学习目的,我使用let条款更新了答案。

现在关于具体的订购。我本可以写出&#34; SortDate&#34;完全按照你的方式行事,但我相信我的方式更好。为什么?

这是我的&#34; SortDate&#34;伪代码中的解释

if (CoSeniorityDate == null)
    SortDate = #2099-01-01#
else if (PreviousCo == "ABC" && CoSeniorityDate < #2001-04-10#)
    SortDate = #2001-04-01#
else
    SortDate = CoSeniorityDate

这是你的功能

if (CoSeniorityDate == null) CoSeniorityDate = #2099-01-01#
if (PreviousCo == "ABC" && CoSeniorityDate < #2001-04-10#)
    SortDate = #2001-04-01#
else
    SortDate = CoSeniorityDate

CoSeniorityDate == null。然后,根据您的逻辑,替换CoSeniorityDate = #2099-01-01#

if (PreviousCo == "ABC" && #2099-01-01# < #2001-04-10#)
    SortDate = #2001-04-01#
else
    SortDate = #2099-01-01#

由于#2099-01-01# < #2001-04-10#始终为 false ,因此它变得简单

SortDate = #2099-01-01#

即。完全像我的标准的第一部分。在else部分,我们已经知道CoSeniorityDate不是 null ,只能检查其他条件。

无论如何,按照你的方式行事就像这样

let sortDate = e.PREV_CO == "ABC" && seniorityDate < baseDate ? minDate : seniorityDate
相关问题