Warning: file_get_contents(/data/phpspider/zhask/data//catemap/5/sql/85.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181

Warning: file_get_contents(/data/phpspider/zhask/data//catemap/7/sql-server/22.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
sql转换自";“每月记录”;至;记录从/到";_Sql_Sql Server_Tsql_Db2 - Fatal编程技术网

sql转换自";“每月记录”;至;记录从/到";

sql转换自";“每月记录”;至;记录从/到";,sql,sql-server,tsql,db2,Sql,Sql Server,Tsql,Db2,我们有一个数据库,用于存储员工每月的价值(例如兼职百分比): 出于报告目的,我需要在“开始/结束”表单中显示值: +-----+---------+---------+----------+ | emp | from | to | parttime | +-----+---------+---------+----------+ | 1 | 2015.01 | 2015.04 | 100 | | 2 | 2015.01 | 2015.01 | 80

我们有一个数据库,用于存储员工每月的价值(例如兼职百分比):

出于报告目的,我需要在“开始/结束”表单中显示值:

+-----+---------+---------+----------+
| emp |  from   |   to    | parttime |
+-----+---------+---------+----------+
|   1 | 2015.01 | 2015.04 |      100 |
|   2 | 2015.01 | 2015.01 |       80 |
|   2 | 2015.02 | 2015.03 |      100 |
|   2 | 2015.04 | 2015.04 |       80 |
|   3 | 2015.01 | 2015.02 |       60 |
|   3 | 2015.03 | 2015.03 |       80 |
|   3 | 2015.04 | 2015.04 |      100 |
+-----+---------+---------+----------+
我的第一次尝试是用一个简单的min/max方法来解决它。但是雇员2号有点棘手,重复值为80

有什么想法/例子吗?数据库基于db/2或microsoft

谢谢


Philipp

根据您的示例数据,我已经在Postgres上测试了这个解决方案,但我几乎可以肯定这将在
DB2
上起作用。它可能需要一些小的改变,但不确定

要一步一步地了解它的工作原理,可以从执行最内部的块开始

SELECT 
    emp, 
    (year||'.'||CASE WHEN length(min_month::text) = 1 THEN '0'||min_month::text ELSE min_month::text END) AS from, 
    (year||'.'||CASE WHEN length(max_month::text) = 1 THEN '0'||max_month::text ELSE max_month::text END) AS to, 
    parttime 
FROM(
    SELECT 
        emp,
        year,
        parttime,
        first_different,
        min(month) AS min_month,
        max(month) AS max_month 
    FROM( 
        SELECT 
            a.*,
            b.* 
        FROM(
            SELECT *
            FROM tablename 
            ) a,
            LATERAL 
            (
            SELECT 
                min(CASE WHEN a.parttime IS DISTINCT FROM b.parttime THEN b.month END) AS first_different
            FROM 
                tablename b 
            WHERE 
                a.emp = b.emp 
                AND a.year = b.year 
                AND a.month < b.month 
            ) b 
        ) foo 
    GROUP BY 1,2,3,4
    ORDER BY 1 
    ) goo 
ORDER BY 1,2;

这就是所谓的差距和岛屿问题。一个快速解决方案:

DECLARE @Employee TABLE
(emp int, year int, month int, parttime int)

INSERT INTO @Employee
VALUES
(1, 2015, 1, 100),
(1, 2015, 2, 100),
(1, 2015, 3, 100),
(1, 2015, 4, 100),
(2, 2015, 1,  80),
(2, 2015, 2, 100),
(2, 2015, 3, 100),
(2, 2015, 4,  80),
(3, 2015, 1,  60),
(3, 2015, 2,  60),
(3, 2015, 3,  80),
(3, 2015, 4, 100)


;WITH cte
AS 
(
    SELECT *
        ,e.[month] - ROW_NUMBER() OVER (ORDER BY e.emp, e.[parttime]) AS Grp
    FROM @Employee e
)
SELECT 
    emp, 
    CAST([year] AS varchar(50)) + '.' + CAST(MIN([month])AS varchar(50)) AS [from],
    CAST([year] AS varchar(50)) + '.' + CAST(MAX([month])AS varchar(50)) AS [to],
    parttime 
FROM cte
GROUP BY emp, parttime, year, Grp
ORDER BY emp, [from]

第一步:检测用户或兼职更改发生的位置(1=更改,0=与最后一行相同的值)。您可以使用分析函数LAG执行此操作

第二步:使用分析函数SUM基于更改标志构建组

第三步:显示每个组的一条记录,其中包含在组中找到的最小年/月和最大年/月

+-----+------+-------+----------+-------+-------+ | emp | year | month | parttime | step1 | step2 | | | | | | chg | grp | +-----+------+-------+----------+-------+-------+ | 1 | 2015 | 1 | 100 | 1 | 1 | | 1 | 2015 | 2 | 100 | 0 | 1 | | 1 | 2015 | 3 | 100 | 0 | 1 | | 1 | 2015 | 4 | 100 | 0 | 1 | | 2 | 2015 | 1 | 80 | 1 | 2 | | 2 | 2015 | 2 | 100 | 1 | 3 | | 2 | 2015 | 3 | 100 | 0 | 3 | | 2 | 2015 | 4 | 80 | 1 | 4 | | 3 | 2015 | 1 | 60 | 1 | 5 | | 3 | 2015 | 2 | 60 | 0 | 5 | | 3 | 2015 | 3 | 80 | 1 | 6 | | 3 | 2015 | 4 | 100 | 1 | 7 | +-----+------+-------+----------+-------+-------+
如果数据库存储的是完整日期,而不是年/月(或至少是等效的组合类型),那么这将更容易实现。或者,如果您可以对原始基础数据进行操作:

SELECT emp, partTime, MIN(monthStart) AS monthStart, MAX(monthNext) AS monthEnd
FROM (SELECT emp, partTime,
             DATEADD(month, month - 1, DATEADD(year, year - 1, CAST('00010101' AS DATE))) AS monthStart,
             DATEADD(month, month, DATEADD(year, year - 1, CAST('00010101' AS DATE))) AS monthNext,
             ROW_NUMBER() OVER(PARTITION BY emp ORDER BY year, month)  -
             ROW_NUMBER() OVER(PARTITION BY emp, partTime ORDER BY year, month) AS groupId
      FROM Monthly_Hours) AS Grouping
GROUP BY emp, partTime, groupId
ORDER BY emp, monthStart

请注意,我特别使用了范围的唯一上限。日期/时间/时间戳类型,就像所有正的、连续的范围类型(除了显式整数计数以外的任何类型)一样,都应该以这种方式进行处理(这使得对它们的推理和查询更加容易)


这个答案有点不足,因为缺少的月份没有直接报告(不显示为
0
)-如果需要,有一些方法可以纠正这个问题,尽管这需要更多的工作。

请不要使用隐式连接语法(逗号分隔的
FROM
子句)。很难或不可能进行
左(外)连接
s,而且很容易忘记一个条件并将其转化为完全笛卡尔积。始终明确列出联接,并在关联中放入尽可能多的相关条件。这在DB/2上也非常有效,谢谢!第一次使用“横向”连接@发条MISE,它被称为“代码>横向子查询< /代码>,这是一个有效的语法@ Philipp很好地知道它的工作原理:请考虑对答案进行投票。我没有抱怨<代码>侧向< /代码>,我抱怨你在前面不放<代码>连接< /代码>(或<代码>交叉连接< /代码>,在这种情况下)。您的答案在循环年份中也没有弹性(特别是,它将为相同的
兼职值创建一个新记录)。使用列索引进行分组/排序(即,
orderby1,2
)通常也被认为是拙劣的风格。@clockworkmuse我同意,这可以使用
x-JOIN-LATERAL
重写。尽管如此,它要求将
emp、年、月
放在
SELECT
语句中,添加
groupby
并解决外部块中的歧义,从而不必要地增加代码并选择您不需要在外部的列。如果您知道自己在做什么,您会注意列表中的列顺序等更改,因此
orderby\u no
可以节省空间。我知道有些数据库管理系统不允许这样做。当数据循环数年时,它就没有弹性了。但是是的,缺口和岛屿。我不知道这叫做缺口和岛屿。谢谢你指出这个术语。我同意Clockwork Muse的观点,这应该考虑一年内开始的范围,另一年结束的范围,但是解决这个问题很容易。一个很好的直截了当的解决方案。我同意你关于存储数据的观点。但我不是db的控制者,我只需要使用给我的东西。好处:我们可以确保数据中没有空白。这么完美的解决我的问题!我认为这个查询无法正常工作,正如您自己的fiddle已经显示的那样(EMP4和parttime 80缺少一个记录)。这里有一个修改过的样本数据来更有力地说明这一点:。@Thorsten-你是对的,尽管他的源数据没有缺口(这导致了这个问题)减轻了这一点。可能是可以修复的,但目前我无法处理。@Philipp-我在我的语句中犯了一个小错误,这可能会在一种情况下产生错误:如果一个值的第一个实例后跟第二个值的第一个实例,那么第一个值的第二个实例。这已经纠正了。@Thorsten-错误纠正了(当我有更好的机会时,我会检查一些关于这个错误的相关答案)。 +-----+------+-------+----------+-------+-------+ | emp | year | month | parttime | step1 | step2 | | | | | | chg | grp | +-----+------+-------+----------+-------+-------+ | 1 | 2015 | 1 | 100 | 1 | 1 | | 1 | 2015 | 2 | 100 | 0 | 1 | | 1 | 2015 | 3 | 100 | 0 | 1 | | 1 | 2015 | 4 | 100 | 0 | 1 | | 2 | 2015 | 1 | 80 | 1 | 2 | | 2 | 2015 | 2 | 100 | 1 | 3 | | 2 | 2015 | 3 | 100 | 0 | 3 | | 2 | 2015 | 4 | 80 | 1 | 4 | | 3 | 2015 | 1 | 60 | 1 | 5 | | 3 | 2015 | 2 | 60 | 0 | 5 | | 3 | 2015 | 3 | 80 | 1 | 6 | | 3 | 2015 | 4 | 100 | 1 | 7 | +-----+------+-------+----------+-------+-------+
select
  emp,
  min(format(year, '0000') + '.' + format(month, '00')) as from_month,
  max(format(year, '0000') + '.' + format(month, '00')) as to_month,
  parttime
from
(
  select
    emp, year, month, parttime,
    sum(chg) over (order by emp, year, month) as grp
  from
  (
    select 
      emp, year, month, parttime, 
      case when lag(emp) over (order by emp, year, month) = emp 
           and lag(parttime) lag(emp) over (order by emp, year, month) = parttime 
        then 0
        else 1
      end as chg
    from mytable
  ) changes
) groups
group by grp, emp, parttime
order by grp;
SELECT emp, partTime, MIN(monthStart) AS monthStart, MAX(monthNext) AS monthEnd
FROM (SELECT emp, partTime,
             DATEADD(month, month - 1, DATEADD(year, year - 1, CAST('00010101' AS DATE))) AS monthStart,
             DATEADD(month, month, DATEADD(year, year - 1, CAST('00010101' AS DATE))) AS monthNext,
             ROW_NUMBER() OVER(PARTITION BY emp ORDER BY year, month)  -
             ROW_NUMBER() OVER(PARTITION BY emp, partTime ORDER BY year, month) AS groupId
      FROM Monthly_Hours) AS Grouping
GROUP BY emp, partTime, groupId
ORDER BY emp, monthStart