简体   繁体   English

将年数和月数转换为月数

[英]Convert number of years and months into number of months

My table holds a column that holds a value that signifies a period in years and months, in other words:我的表包含一个列,其中包含一个值,该值表示以年和月为单位的时间段,换句话说:

ID  PERIOD
1   1Y
2   1Y1M
3   11M
4   5Y2M

When doing a select statement how to I convert/calculate this into number of months?在执行选择语句时如何将其转换/计算为月数? It's easy to deal with values that are 'Y' only or 'M' only but not sure how to do for example ID 2 and ID 4 from the example above.处理只有 'Y' 或只有 'M' 的值很容易,但不知道如何处理上面示例中的 ID 2 和 ID 4。

The result from the select statement if I'd select all above would be:如果我选择以上所有内容,select 语句的结果将是:

12
13
11
62

You can use您可以使用

SUBSTRING(period, 1, CHARINDEX('Y', Period) - 1)

To get only the numbers before the Y, and then multiply it by 12.只得到 Y 之前的数字,然后乘以 12。

And for the cases where the Y would not be present could be handled with CASE对于 Y 不存在的情况可以用 CASE 处理

So Something like:所以像这样:

   SELECT  CASE WHEN Period LIKE '%Y' THEN
             CAST(SUBSTRING(period, 1, CHARINDEX('Y', Period) - 1)) * 12 
             +
             CAST(REPLACE(SUBSTRING(period, CHARINDEX('Y', Period)),'M',''))
           ELSE
             CAST(REPLACE(period,'M',''))

You can do the following您可以执行以下操作

SELECT *, TRY_CAST(REPLACE(Y, 'Y', '') AS INT) * 12 + 
          TRY_CAST(REPLACE(REPLACE(Period, Y, ''), 'M', '') AS INT)
FROM
(
  VALUES
  (1,   '1Y'),
  (2,   '1Y1M'),
  (3,   '11M'),
  (4,   '5Y2M'),
  (5, 'Whatever')
) T(Id, Period)
CROSS APPLY
(
  VALUES
  (LEFT(Period, CHARINDEX('Y', Period)))
) CI(Y)

Returns:返回:

+----+----------+----+------------------+
| Id |  Period  | Y  | (No column name) |
+----+----------+----+------------------+
|  1 | 1Y       | 1Y |               12 |
|  2 | 1Y1M     | 1Y |               13 |
|  3 | 11M      |    |               11 |
|  4 | 5Y2M     | 5Y |               62 |
|  5 | Whatever |    |                  |
+----+----------+----+------------------+

Here is an other way这是另一种方式

SELECT Id, 
       Period, 
       (REPLACE(Years, 'Y', '') * 12) + REPLACE(Months, 'M', '') TotalMonths
FROM
(
  VALUES
  (1, '1Y'),
  (2, '1Y1M'),
  (3, '5M'),
  (4, '10Y11M'),
  (5, 'Whatever write Y and M')
) T(Id, Period)
CROSS APPLY
(
  VALUES
  (
    LEFT(Period, CHARINDEX('Y', Period)), REPLACE(REPLACE(Period, 'Y', ''), 'M', '')
  )
) TT(Years, Value)
CROSS APPLY
(
  VALUES
  (
    REPLACE(Period, Years, '')
  )
) TTT(Months)
WHERE TRY_CAST(TT.Value AS INT) IS NOT NULL;

db-fiddle 数据库小提琴

There aren't so many possibilities.没有那么多可能。 Build a reference table:建立参考表:

select identity(int) as period_id, v.*
into periods p
from (values ('1M', 1),
             ('2M', 2),
             . . . 
             ('1Y', 12),
             ('1Y1M', 13),
             . . .
    ) v(period, months);

This can easily be constructed using a spreadsheet.这可以使用电子表格轻松构建。 Or even a recursive CTE.甚至是递归 CTE。

I am suggesting this for a serious reason: you should not be doing calculations on string representations like this.我建议这样做的原因很严重:你不应该像这样对字符串表示进行计算。 These values should be treated as foreign key references to a table.这些值应被视为对表的外键引用。 And, in fact, they should be using the primary key (the identity column) rather than the string.而且,事实上,他们应该使用主键(标识列)而不是字符串。

You will probably find malformed strings as you go about fixing this.在修复此问题时,您可能会发现格式错误的字符串。 That is a good thing, from a data quality perspective.从数据质量的角度来看,这是一件好事。

Another alternative另一种选择

select id, (parsename (clean,2) * 12) + (parsename(clean,1)) as months
from t
cross apply (select replace(replace(case when charindex('M', period)=0 then period + '0M' 
                                         when charindex('Y', period)=0 then '0Y' + period
                                         else period end,'M',''),'Y','.') as clean) t2

Outputs输出

+----+--------+
| id | months |
+----+--------+
|  1 |     12 |
|  2 |     13 |
|  3 |     11 |
|  4 |     62 |
+----+--------+

I think something like this would work我认为这样的事情会起作用

Data数据

drop table if exists #tTEST;
go
select * INTO #tTEST from (values 
(1, '1Y'),
(2, '1Y1M'),
(3, '11M'),
(4, '5Y2M')) V(ID, [Period]);

Query询问

select
  t.*, 
  isnull(substring(t.[Period], 1, nullif(y_ndx, 0)-1)*12, 0) +
  isnull(substring(t.[Period], y_ndx+1, nullif(m_ndx, 0)-isnull(y_ndx, 0)-1),0) answer
from #tTEST t
     cross apply (select len(t.[Period]) p_len,
                         CHARINDEX('Y', t.[Period]) y_ndx,
                         CHARINDEX('M', t.[Period]) m_ndx) ndx;

Results结果

ID  Period  answer
1   1Y      12
2   1Y1M    13
3   11M     11
4   5Y2M    62

Try this below Scalar-Valued Function to get period value在标量值函数下面试试这个以获得周期值

CREATE FUNCTION [dbo].[fn_sum](  
@delimited NVARCHAR(MAX),  
@delimiter NVARCHAR(100)  
) RETURNS INT 
AS  
BEGIN    
DECLARE @value INT
set @delimited=replace(replace(@delimited,'M','M,'),'Y','Y,')
DECLARE @xml XML  
SET @xml = N'<t>' + REPLACE(@delimited,@delimiter,'</t><t>') + '</t>'  

SELECT @value=sum(cast(replace(replace(r.value('.','Nvarchar(MAX)'),'M',''),'Y','') AS INT) 
* CASE WHEN right(r.value('.','Nvarchar(MAX)'),1)='M' THEN 1 ELSE 12 END) 
FROM @xml.nodes('/t') AS records(r)  

RETURN  @value
END 

Check below sample output检查以下示例输出

DECLARE @tblPeriod AS TABLE(id VARCHAR(100),period VARCHAR(100))
INSERT INTO @tblPeriod(id,period) VALUES(1,'1Y'),(2,'1Y1M'),(3,'11M'),(4,'5Y2M') 

SELECT id,period,dbo.[fn_sum](period,',') AS period_value  FROM @tblPeriod a

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM