4

我想将 SQL Server 中的现有列更改为计算并保留而不删除表/列。

我有一个自动递增的ID列和另一个ReportID用这个计算格式化的列:

ReportID = 'RPT'+{FiscalYear}+{0s}+{Auto Incremented ID}

例如:

  • 对于 ID = 1, 2, ...., 10, 11, ..., 100, 101
  • ReportID 将是RPT2122000001, RPT2122000002,..., RPT2122000010, RPT2122000011, ..., RPT2122000101,RPT2122000102

以前,我们在“插入后”触发器中执行此操作 - 计算值并更新行。但是通过这样做,当负载很高并且报告由不同的用户并行生成时,一些 ReportID 会被复制。

因此,为了解决这个问题,我想将现有列更改为计算列,'RPT'+{FiscalYear}+{0s}+{Auto Incremented ID}但问题是我希望现有数据保持不变。因为如果现在运行计算,所有上一年的数据都将被修改为当前财政年度,这是错误的。

当我通过在 Management Studio 中设置计算值直接尝试时,它也在内部运行 drop 并在后台重新添加。

我看到了很多答案,但他们还不够满意。

我尝试使用计算值创建一个新列,然后尝试重命名,这也是不允许的。

编辑 1

错误:

Computed column 'ReportID' in table 'Tmp_wp_tra_report_creation' cannot be persisted because the column is non-deterministic

编辑 2

财政年度计算:

(select (case when ((select top 1 a.Pc_FromDate from wp_pc_calendar a where a.Pc_FromDate>=getdate())<=getdate()) then (select top 1 b.Pc_FinYear from wp_pc_calendar b where b.Pc_FromDate>=getdate() order by b.Pc_FromDate asc ) else (case when ((select top 1 c.Pc_FromDate from wp_pc_calendar c where c.Pc_FromDate<=getdate() )<=getdate()) then (select top 1 d.Pc_FinYear from wp_pc_calendar d where d.Pc_FromDate<=getdate() order by d.Pc_FromDate desc ) else 'No Records' end) end) as finyear)

另外,有没有不创建新列的方法?

4

4 回答 4

1

由于您正在计算会计年度,因此您可以将会计年度计算放在标量函数中并在您的计算列中使用它:

  1. 会计年度计算的函数:
CREATE FUNCTION fiscalyear(@currentdate AS datetime)
RETURNS int 
AS BEGIN 
   @fiscalyear int = <yourlogic here >
 RETURN @fiscalyear
end

但是,在您的会计年度计算中,您应该使其取决于输入参数日期(而不是 getdate()),并且在您的计算列中,您为该行传递诸如 reportdate 或 insertdate 之类的内容。

因为如果您的计算基于 getdate() ,计算列的值将在明年更改为同一行。这不是你想要的。

也使用函数将被视为非确定性并且计算列不能被持久化。

然后跟进其他人所说的:

  1. 重命名旧列:
exec sp_rename 'test_reports.report_id', 'original_report_id'
  1. 添加另一列以保存报告日期
alter table tablename
add reportdate datetime not null default (getdate())

您可以为其设置默认值,无需修改插入/更新

  1. 添加新的计算列:
alter table test_reports add report_id as (
    coalesce(original_report_id,
             'RPT' + convert(varchar,dbo.fiscalyear(reportdatecolumn) + right('000000' + convert(varchar, id), 6)))
于 2021-05-17T18:03:12.820 回答
1

我想通过以下方式解决这个问题:

  1. 重命名原始列
  2. 创建使用原始列的新计算列,如果原始列没有值(为空),则故障转移到计算。

喜欢:

-- === Setting up some data for testing
drop table if exists test_reports
create table test_reports (
  id int identity(1, 1),
  fiscal_year int,
  report_id varchar(15)
)
insert test_reports (fiscal_year, report_id) values (2122, 'RPT2122000001')
insert test_reports (fiscal_year, report_id) values (2122, 'RPT2122000002')
insert test_reports (fiscal_year, report_id) values (2122, 'RPT2122000010')
insert test_reports (fiscal_year, report_id) values (2122, 'RPT2122000011')
insert test_reports (fiscal_year, report_id) values (2122, 'RPT2122000101')
insert test_reports (fiscal_year, report_id) values (2122, 'RPT2122000102')

-- === Rename the existing column to something else because it will start to 
-- === contain nulls and the nulls may break existing code.
exec sp_rename 'test_reports.report_id', 'original_report_id'

-- === Add our new, computed column that checks the original column
-- === for a value and uses the original value, if available.  Otherwise,
-- === the computed column is an actual computation.
alter table test_reports add report_id as (
    coalesce(original_report_id,
             'RPT' + convert(varchar, fiscal_year) + right('000000' + convert(varchar, id), 6)))


insert test_reports(fiscal_year) values (2123)

select * from test_reports
于 2021-05-17T02:56:44.753 回答
1

在我的例子中

尝试这个,

drop table if exists test_reports
create table test_reports (
  id int identity(1, 1),
  col1 varchar(10),
  report_id varchar(15)
)
insert test_reports (col1,report_id) values ('çol1', 'RPT2122000001')
insert test_reports (col1,report_id) values ('çol1', 'RPT2122000002')
insert test_reports (col1,report_id) values ('çol1', 'RPT2122000010')
insert test_reports (col1,report_id) values ('çol1', 'RPT2122000011')
insert test_reports (col1,report_id) values ('çol1', 'RPT2122000101')
insert test_reports (col1,report_id) values ('çol1', 'RPT2122000102')

步骤1

, 重命名旧列

sp_rename 'test_reports.report_id', 'Oldreport_id', 'COLUMN';

第 2 步:获取最大自动递增的 id 列值。

第 3 步:

ALTER TABLE test_reports

    ADD report_id AS CASE
                         WHEN id > 6
                         THEN 'RPT' + CAST(YEAR(GETDATE()) AS VARCHAR) + CAST(id AS VARCHAR)
                         ELSE Oldreport_id
                     END;

第 4 步尝试新插入

 insert test_reports (col1) values ('çol1')
    select * from test_reports

您可以使用自己的会计年度逻辑,它会起作用。

于 2021-05-17T10:39:30.673 回答
1

很久以前,我遇到了与您类似的问题,以下解决方案对我有用。

您可能会遇到此问题,因为在批量插入的情况下,它只会使用上次更新的值更新列的值(对于所有行),因此您会得到重复的值。

考虑到您现有的结构 -在插入触发器之后,一种方法是继续使用触发器并尝试遍历 INSERTED 表

IF (OBJECT_ID('tempdb..#T') IS NOT NULL)
BEGIN
    DROP TABLE #T
END

CREATE TABLE #T (
    [Serial] INT IDENTITY(1, 1)
    ,[ID] INT
    ,[ReportID] VARCHAR(100) -- data type you are using 
    )

INSERT INTO #T (
    [ID]
    ,[ReportID]
    )
SELECT [ID]
    ,[ReportID]
FROM INSERTED

DECLARE @LoopCounter INT
    ,@MaxId INT
    ,@ID INT
    ,@ReportID VARCHAR(100)

SELECT @LoopCounter = MIN([Serial])
    ,@MaxId = MAX([Serial])
FROM #T

WHILE (
        @LoopCounter IS NOT NULL
        AND @LoopCounter <= @MaxId
        )
BEGIN
    SELECT @ID = [ID]
        ,@ReportID = [ReportID]
    FROM #T
    WHERE [Serial] = @LoopCounter

    /*  write your logic here, you can use @ID to identify your record
        ReportID = 'RPT'+{FiscalYear}+{0s}+{Auto Incremented ID}
    */

    SELECT @LoopCounter = min([Serial])
    FROM #T
    WHERE [Serial] > @LoopCounter
END

    
于 2021-05-23T07:36:05.520 回答