【问题标题】:Listing number sequence for financial periods财务期间的清单编号顺序
【发布时间】:2017-10-30 13:26:11
【问题描述】:

在 SQL 2016 中,我需要使用财务期间创建一个列表,但只有从/到可用 - 它的格式类似于日期,但为 0mmyyyy,因此前 3 个数字是月份/期间,最后 4 位数字是年。

例如period_from 是 '0102017' 和 period_to '0032018',但要恢复一个包含两者之间的列表?

0102017, 
0112017, 
0122017, 
0012018,
0022018

另外,前三个字符可以转到012或013,所以需要能够轻松更改其他数据库的代码。

【问题讨论】:

  • 不清楚您需要什么。向我们展示数据库架构、示例数据、当前和预期输出。请阅读How-to-Ask 这里是START 了解如何提高问题质量并获得更好答案的好地方。 How to create a Minimal, Complete, and Verifiable example 尝试在rextester.com 中创建样本
  • 您好,表中有两个字段:PERIODS PERIOD_FROM(CHAR_CODE_N8(nchar(8)) PERIOD_TO(CHAR_CODE_N8(nchar(8)),上面的示例所需输出
  • 你能把格式改成YYYY0MM吗?那么很简单WHERE period_from BETWEEN '2017010' AND '2018002'
  • '013'是什么时期?
  • @ Juan - 格式是固定的,但我可以输出该字段,但我想我也会得到大约 900 行其他行,例如2017015,016 等我不想要的。期间 013 介于 012 和 001 之间,(对于某些无法进入期间的财务成本)

标签: sql-server financial period listings


【解决方案1】:

我不完全确定您要将此列表用于什么目的,但您可以借助计数表和一些常用表表达式获取所有 period 值。

-- Test data
declare @p table(PeriodFrom nvarchar(10),PeriodTo nvarchar(10));
insert into @p values('0102017','0032018'),('0052018','0112018');

-- Specify the additional periods you want to include, use 31st December for correct sorting
declare @e table(ExtraPeriodDate date
                ,ExtraPeriodText nvarchar(10)
                );
insert into @e values('20171231','0132017');

                        -- Convert start and end of periods to dates
with m    as (select cast(min(right(PeriodFrom,4) + substring(PeriodFrom,2,2)) + '01' as date) as MinPeriod
                    ,cast(max(right(PeriodTo,4) + substring(PeriodTo,2,2)) + '01' as date) as MaxPeriod
              from @p
             )          -- Built a tally table of dates to join from
    ,t(t) as (select 1 union all select 1 union all select 1 union all select 1 union all select 1 union all select 1 union all select 1 union all select 1 union all select 1 union all select 1)
    ,d(d) as (select top (select datediff(month,MinPeriod,MaxPeriod)+1 from m) dateadd(m,row_number() over (order by (select null))-1,m.MinPeriod) from m, t t1, t t2, t t3, t t4, t t5)
                        -- Use the tally table to convert back to your date period text format
    ,p    as (select d.d as PeriodDate
                    ,'0' + right('00' + cast(month(d) as nvarchar(2)),2) + cast(year(d) as nvarchar(4)) as PeriodText
              from d
              union all -- and add in any of the addition '13th' month periods you specified previously
              select ExtraPeriodDate
                      ,ExtraPeriodText
              from @e
             )
select PeriodText
from p
order by PeriodDate;

输出:

+------------+
| PeriodText |
+------------+
|    0102017 |
|    0112017 |
|    0122017 |
|    0132017 |
|    0012018 |
|    0022018 |
|    0032018 |
|    0042018 |
|    0052018 |
|    0062018 |
|    0072018 |
|    0082018 |
|    0092018 |
|    0102018 |
|    0112018 |
+------------+

如果这不是您所需要的,那么它应该让您走上正确的道路,以生成这些值作为函数的结果,或者根据您的评论通过在结果上使用 for xml 将这些值串联到一个列表中将最后的 select 语句更改为:

select stuff((select ', ' + PeriodText
              from p
              order by PeriodDate
              for xml path('')
             )
             ,1,2,'') as PeriodTexts;

哪些输出:

+---------------------------------------------------------------------------------------------------------------------------------------+
|                                                              PeriodTexts                                                              |
+---------------------------------------------------------------------------------------------------------------------------------------+
| 0102017, 0112017, 0122017, 0132017, 0012018, 0022018, 0032018, 0042018, 0052018, 0062018, 0072018, 0082018, 0092018, 0102018, 0112018 |
+---------------------------------------------------------------------------------------------------------------------------------------+

【讨论】:

    【解决方案2】:

    这会有点复杂。首先,我有一个用户定义的表值函数,它根据开始和结束日期输出日历表。您需要先创建它...

    CREATE FUNCTION dbo.udf_calendar (@datestart smalldatetime, @dateend smalldatetime)
    RETURNS @calendar TABLE (
      [day] int,
      [date] smalldatetime
    )
    AS
    
    BEGIN
    
      DECLARE @rows int
      DECLARE @i int = 1
    
      SELECT
        @rows = DATEDIFF(DAY, @datestart, @dateend)
    
      WHILE (@i <= @rows)
      BEGIN
    
        INSERT INTO @calendar ([day])
          VALUES (@i)
    
        SET @i = @i + 1
    
      END
    
      UPDATE a
      SET [date] = DATEADD(DAY, [day] - 1, @datestart)
      --select *, DATEADD(day,id-1,@datestart)
      FROM @calendar a
    
      RETURN
    END
    

    然后,以下将为您提供我认为您正在寻找的输出。我已经发表了评论,试图解释我是如何到达那里的,但仍然可能有点难以理解......

    --Create temp table example with your period from and to.
    
    IF (SELECT
        OBJECT_ID('tempdb..#example'))
      IS NOT NULL
      DROP TABLE #example
    
    SELECT
      '0102017' periodfrom,
      '0032018' periodto INTO #example
    
    /*
    
    This is the difficult part. Basically you're inner joining the calendar
    to the temp table where the dates are between the manipulated period from and to.
    I've added an extra column formatted to allow ordering correctly by period.
    
    */
    
    SELECT DISTINCT
      periodfrom,
      periodto,
      RIGHT('00' + CAST(DATEPART(MONTH, [date]) AS varchar(50)), 3) + CAST(DATEPART(YEAR, [date]) AS varchar(50)) datefill,
       CAST(DATEPART(YEAR, [date]) AS varchar(50)) + RIGHT('00' + CAST(DATEPART(MONTH, [date]) AS varchar(50)), 3) datefill2
    FROM dbo.udf_calendar('2015-01-01', '2018-12-31') a
    INNER JOIN #example b
      ON a.[date] BETWEEN SUBSTRING(periodfrom, 2, 2) + '-01-' + SUBSTRING(periodfrom, 4, 4) AND SUBSTRING(periodto, 2, 2) + '-01-' + SUBSTRING(periodto, 4, 4)
    ORDER BY datefill2
    

    【讨论】:

    • 我刚刚意识到我的解决方案无法处理“月”013。这令人失望。我会继续考虑的。
    猜你喜欢
    • 2018-04-27
    • 2021-04-07
    • 2012-06-11
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2018-11-03
    • 1970-01-01
    相关资源
    最近更新 更多