在SQL(T-SQL)中进行高级逻辑 - 匹配算法

时间:2014-10-03 13:59:25

标签: sql sql-server algorithm tsql stored-procedures

好的,所以这个特殊的用例让我有点头疼。

这是另一篇文章的编辑,我忘记在用例中添加一个重要方面。

需要做的是将候选人 WorkWish 与数据库中的 CustomerJobQueries 相匹配。

这意味着,如果 WorkWish 中的日期时间与任何 CustomerJobQueries 重叠,则匹配。然后我需要将 GeographicalPreference 与客户的位置相匹配。

我们构建数据库时的逻辑是:

  • 如果国家已设置但区域和市政当局为空 - 候选人希望在整个国家/地区工作。
  • 如果国家和地区设置,候选人希望在特定国家/地区的特定地区的所有城市工作。
  • 如果国家,地区和市政当局已经确定,他希望只在该市工作。

可能存在多个行,因此设置了2个特定的城市,唯一的规则是永远不会存在优先于另一个的行。

含义

GeographicalPreferenceId Country_ShortName    Region_Id   Municipality_Id WorkWish_Id
------------------------ -------------------- ----------- --------------- -----------
1                        DK                   NULL        NULL            1
2                        DK                   3           NULL            1

不可能,因为有一行已经指明他希望在整个国家/地区工作。

这就是数据库片段的样子

DateRange表 - 包含所有DateRanges

DateRangeId StartDate                   EndDate                     CustomerJobQuery_Id WorkWish_Id
----------- --------------------------- --------------------------- ------------------- -----------
1           2014-03-31 00:00:00.0000000 2014-08-18 00:00:00.0000000 NULL                1
2           2014-08-25 00:00:00.0000000 2014-09-15 00:00:00.0000000 NULL                1
3           2013-08-24 00:00:00.0000000 2014-09-25 00:00:00.0000000 1                   NULL

工作愿望表

Id          Candidate_Id
----------- ------------
1           5

CustomerJobQuery表

Id          Customer_CustomerId Country_ShortName  Region_Id  Municipality_Id
----------- ------------------- -----------------  ---------  -----------------
1           2                   DK                 2          3

地理位置表

GeographicalPreferenceId Country_ShortName    Region_Id   Municipality_Id WorkWish_Id
------------------------ -------------------- ----------- --------------- -----------
1                        DK                   2           NULL            1
2                        DK                   3           NULL            1

到目前为止是什么; - 返回与WorkWish日期重叠的CustomerJobQueries。

SELECT c.*
 FROM CustomerJobQuery c
  WHERE EXISTS ( 
   SELECT *
     FROM Workwish w
     INNER JOIN DateRange d1 ON d1.CustomerJobQuery_Id = c.Id
     INNER JOIN DateRange d2 ON d2.WorkWish_Id = w.Id
     WHERE w.Candidate_Id = @CandidateId -- input from my **StoredProcedure**
     -- overlap check:
     AND d1.StartDate <= d2.EndDate
     AND d2.StartDate <= d1.EndDate

我不确定如何以集合方式执行地理偏好逻辑。 - 目前我们的解决方案是在C#中将所有内容加载到内存中,并使用嵌套的foreach循环遍历所有内容,这非常无效。并且是绝对最糟糕的情况。

非常感谢任何帮助!

2 个答案:

答案 0 :(得分:1)

我创建了一些表,因为你没有提供任何DDL。

declare @CandidateId int;

declare @WorkWish table (
    Id int primary key,
    CandidateId int
);

declare @Preference table (
    Id int primary key,
    WishId int,
    Country char(2),
    RegionId int null,
    MunicipalityId int null
);

declare @DateRange table (
    Id int primary  key,
    StartDate datetime,
    EndDate datetime,
    JobQueryId int null,
    WishId int null
);

declare @JobQuery table (
    Id int primary key,
    CustomerId int,
    Country char(2),
    RegionId int,
    MunicipalityId int
);


select *,
    case
        when p.MunicipalityId is not null then 0
        when p.RegionId is not null then 1
        else 2
    end as [LocationMetric]
from @WorkWish w
    inner join @Preference p on w.Id = p.WishId
    inner join @DateRange dw on dw.WishId = w.Id
    inner join @JobQuery j on j.Country = p.Country
    inner join @DateRange dj on dj.JobQueryId = j.Id
where w.CandidateId = @CandidateId
    and dw.StartDate <= dj.EndDate
    and dj.StartDate <= dw.EndDate
    and nullif(p.RegionId, j.RegionId) is null
    and nullif(p.MunicipalityId, j.MunicipalityId) is null
order by LocationMetric;

我必须警告你关于NULLIF()函数的性能,但是它还远非完美。您可以尝试将其扩展为相应的case构造,但通常不值得努力。

不过,它应该比你现在拥有的更好。

答案 1 :(得分:0)

我认为以下内容将为您提供所有匹配:

WITH ww as (
      SELECT ww.*, drw.StartDate, drw.EndDate
      FROM WorkWish ww JOIN
           DateRange drw
           ON drw.WorkWish_Id = w.id
     )
SELECT c.*
FROM CustomerJobQuery c JOIN
     DateRange drc
     ON drc.CustomerJobQuery_Id = c.id JOIN
     ww
     ON drc.StartDate <= ww.EndDate AND ww.StartDate <= drw.EndDate AND
        (drc.Country_ShortName = ww.Country_ShortName and ww.Region_id is NULL or
         drc.Country_ShortName = ww.Country_ShortName and drc.Region_id = ww.Region_id and ww.municipality_id is null or
         drc.Country_ShortName = ww.Country_ShortName and drc.Region_id = ww.Region_id and drc.Municipality_Id = ww.Munipality_Id
        )
)

如果要确定匹配的优先级,请使用row_number()和子查询:

WITH ww as (
      SELECT ww.*, drw.StartDate, drw.EndDate
      FROM WorkWish ww JOIN
           DateRange drw
           ON drw.WorkWish_Id = w.id
     )
SELECT c.*
FROM (SELECT c.*, ww.id as WorkId,
             ROW_NUMBER() OVER (PARTITION BY c.id
                                ORDER BY (case when ww.Munipality_Id is not null then 1
                                               when ww.Region_id is not null then 2
                                               else 3
                                          end)
                               ) as seqnum
      FROM CustomerJobQuery c JOIN
           DateRange drc
           ON drc.CustomerJobQuery_Id = c.id JOIN
           ww
           ON drc.StartDate <= ww.EndDate AND ww.StartDate <= drw.EndDate AND
              (drc.Country_ShortName = ww.Country_ShortName and ww.Region_id is NULL or
               drc.Country_ShortName = ww.Country_ShortName and drc.Region_id = ww.Region_id and ww.municipality_id is null or
               drc.Country_ShortName = ww.Country_ShortName and drc.Region_id = ww.Region_id and drc.Municipality_Id = ww.Munipality_Id
              )
     ) c
WHERE seqnum = 1;