3

也许问题的标题不合适,但这里是解释:

我有以下表格:

在此处输入图像描述

只有 5 个优惠代码可用:

在此处输入图像描述

因此,一名员工可以拥有 1 到 5 项福利,但也可以是没有任何福利的员工。

我需要在查询中返回一个员工列表,其中包含相关福利的编码列,如下例所示:

在此处输入图像描述

因此,“福利”列是来自员工相关福利的编码列。

如果 Peter 有相关的医疗教育福利,则“福利”列的编码值应如表“01001”中所示,其中 0 表示无关联,1 表示关联。

现在我正在做follogin并且正在工作,但处理时间太长,我相信有更好更快的方法:

SELECT emp.employee_id, emp.name, emp.lastname, 
CASE WHEN lif.benefitcode IS NULL THEN '0' ELSE '1' END +
CASE WHEN med.benefitcode IS NULL THEN '0' ELSE '1' END +
CASE WHEN opt.benefitcode IS NULL THEN '0' ELSE '1' END +
CASE WHEN uni.benefitcode IS NULL THEN '0' ELSE '1' END +
CASE WHEN edu.benefitcode IS NULL THEN '0' ELSE '1' END as benefits
FROM employee emp
-- life
LEFT JOIN ( 
     SELECT c.benefitcode, c.employee_id
     FROM employee_benefit c
     WHERE c.isactive = 1
     and c.benefitcode = 'lf'
) lif on lif.employee_id = emp.employee_id
-- medical
LEFT JOIN ( 
     SELECT c.benefitcode, c.employee_id
     FROM employee_benefit c
     WHERE c.isactive = 1
     and c.benefitcode = 'md'
) med on med.employee_id = emp.employee_id
-- optical
LEFT JOIN (
     SELECT c.benefitcode, c.employee_id
     FROM employee_benefit c
     WHERE c.isactive = 1
     and c.benefitcode = 'op'
) opt on opt.employee_id = emp.employee_id
-- uniform
LEFT JOIN (
     SELECT c.benefitcode, c.employee_id
     FROM employee_benefit c
     WHERE c.isactive = 1
     and c.benefitcode = 'un'
) uni on uni.employee_id = emp.employee_id
-- education
LEFT JOIN (
     SELECT c.benefitcode, c.employee_id
     FROM employee_benefit c
     WHERE c.isactive = 1
     and c.benefitcode = 'ed'
) edu on edu.employee_id = emp.employee_id

关于什么是具有最佳性能的最佳方法的任何线索?

非常感谢。

4

4 回答 4

1

为什么不加入一个将福利编码为整数的表(生命 -> 10000,医疗 -> 1000,...,教育 -> 1;然后

  • 将福利代码整数相加;
  • 将总和转换为字符串;
  • 在字符串 '0000' 前面加上最右边的 5 个字符。

更新:

select
   EmployeeID,
   right('0000' + convert(varchar(5),sum(map.value)),5)
from (
    select value=10000, benefit = 'Lif' union all
    select value= 1000, benefit = 'Med' union all
    select value=  100, benefit = 'Uni' union all
    select value=   10, benefit = 'Opt' union all
    select value=    1, benefit = 'Edu'
) map
join
   blah blah 
group by EmployeeID
于 2013-07-08T03:10:18.940 回答
0

首先,请注意此操作实际上对模型进行了反规范化,如果这是我的决定,我将保留规范化的表设计。我不确定是什么“压力”要求这种情况,但我发现这种方法使模型更难维护和使用。这种反规范化可能会减慢原本可以在连接表上使用索引的查询。

话虽如此,一种方法是使用PIVOT,它是 SQL Server (2005+) 扩展。我已经在 sqlfiddle 上编写了一个示例。该示例需要定制一些实际的表模式和福利值 - 在这种情况下,数据透视表位于链接 (employee_benefit) 表之上。请注意福利状态的预过滤器,以避免列(以及因此隐式分组)从 PIVOT 中蔓延。

询问

select pvt.*
from (
  select emp, benefitcode
  from benefits
  where isactive = 1
) as b
pivot (
  -- implicit group by on other columns!
  count (benefitcode)
  -- the set of all values (to turn into columns)
  for benefitcode in ([lf], [md], [op])
) as pvt

定义

create table benefits (
  emp int,
  isactive tinyint,
  benefitcode varchar(10)
)
go

insert into benefits (emp, isactive, benefitcode)
values
(1, 0, 'lf'), (1, 1, 'md'), (1, 1, 'op'),
(2, 1, 'lf'),
(3, 1, 'lf'), (3, 1, 'md'), (3, 1, 'md')
go

结果

EMP LF  MD  OP
1   0   1   1    -- excludes inactive LF for emp #1
2   1   0   0
3   1   2   0    -- counts multiple benefits

请注意,就像许多左连接一样,福利数据现在是针对一组固定值的列。然后只需操作来自结果查询的数据(例如,在原始代码中完成,但检查 >= 1)以构建所需的位数组值。

它会表现得更好吗?

我不知道。但是,我的“初步预感”是,如果员工列被索引但好处不是,查询可能会执行得更好;并且如果反过来,它的性能可能会更差 -检查这两种方法的查询计划以了解 SQL Server真正在做什么。

于 2013-07-08T03:53:53.750 回答
0

我喜欢求和建议,但我会这样内联:

Select
   e.employee_id,
   e.Name,
   e.Lastname,
   right('0000' + convert(varchar(5),sum( 
        case 
           when eb.benefitcode is null then 0
           when eb.benefitcode = 'lf' then 10000
           when eb.benefitcode = 'md' then 1000
           when eb.benefitcode = 'op' then 100
           when eb.benefitcode = 'un' then 10
           when eb.benefitcode = 'ed' then 1
        end )),5) benefits
from
   Employee e

   LEFT OUTER JOIN Employee_Benefit eb
      on ( eb.Employee_id = e.Employee_id )
group by
   e.employee_id,
   e.Name,
   e.Lastname

没有机会尝试它的语法,但这是一般的想法。

于 2013-07-08T04:20:16.233 回答
0
WITH CTE (EMPID,EMPNAME,LASTNAME) AS (SELECT D.* FROM TABLENAME_EMP D WHERE D.EMPID=1), CTE2(BENEFITS) AS ((SELECT SUBSTRING((SELECT ''+ C.BENEFITS FROM (SELECT A.* ,B.BEN_ID,CASE WHEN B.BEN_ID 为 NULL THEN '0' ELSE '1' END BEN_EMP b JOIN TABLENAME_EMP A ON A.empid=b.empid WHERE b.EMPID IN (1) AND B.ben_id IN ( 1,2,3,4,5)) C 的 C 指令,用于 XML 路径的好处 ('')),1,200000) 作为好处))
从 cte、cte2 中选择 *
于 2013-07-08T08:09:07.277 回答