使用其他列生成新列

Posted

技术标签:

【中文标题】使用其他列生成新列【英文标题】:generate new column using other columns 【发布时间】:2011-05-17 20:16:46 【问题描述】:

我无法生成一个新列。该表有三列(C_ID、C_rank、Date)。

C_ID C_ Rank NewColumn(Cycle) Date
 42     A        1            October 14, 2010
 42     B        1            October 26, 2010
 42     A        2            February 16, 2011

 43     A        1            December 17, 2010

 44     A        1            July 28, 2010
 44     B        1            August 10, 2010
 44     A        2            January 11, 2011
 44     B        2            January 28, 2011

 45     A        1            July 30, 2010
 45     B        1            August 9, 2010
 45     B        1            September 24, 2010
 45     A        2            April 5, 2011
 45     B        2            April 26, 2011

我想再生成一个名为Cycle 的列,这样对于每个C_ID,它应该生成从一开始的数字并从下一个C_rank = 'A' 开始递增数字(如上所示)。

我尝试使用 row_number,但没有成功。

也许一些循环选项直到下一个C_Rank = 'A' 有效。

如何做到这一点?

【问题讨论】:

我一直追到 '45 组中的几行。 45 B 1 September 24, 20101 不应该是2 吗? 45 B 2 April 26, 2011 不应该是 3 吗? 【参考方案1】:

您应该能够使用ROW_NUMBER()PARTITION BY 完成此操作

;WITH YourDataCTE AS
(
   SELECT
      C_ID, C_Rank, Date,
      ROW_NUMBER() OVER(PARTITION BY C_ID,C_Rank ORDER BY Date DESC) AS 'Cycle'
   FROM
      dbo.YourTable
)
SELECT *
FROM YourDataCTE

这是否符合您的要求??

PARTITION BY C_ID,C_Rank 将导致ROW_NUMBER 对于C_ID,C_Rank 的每个不同值再次从 1 开始 - 我不知道单个分区中的 ORDER BY 子句(C_ID,C_Rank 的单个值)您正在寻找并且只是猜测它可能是Date DESC(最新日期优先)。

【讨论】:

这会为C_ID, Rank 组中的每一行分配递增的数字。不是问题所要求的 @Andomar: 嗯....是的,似乎 OP 的要求比这更复杂....不确定如何轻松满足该要求....【参考方案2】:

您可以在子查询中计算之前A 的数量:

select  *
,       (
        select  count(*)
        from    @YourTable yt2
        where   yt2.C_ID = yt1.C_ID
                and yt2.C_Rank = 'A'
                and yt2.Date <= yt1.Date
        ) as Cycle
from    @YourTable yt1
order by
        C_ID, Date

Example at ODATA.

【讨论】:

【参考方案3】:

对具有相同 C_ID、上一个日期和 C_Rank='A' 的所有记录进行自联接并计算它们。

select t1.C_ID, t1.C_Rank, count(t2.C_Rank) Cycle, t1.Date
from MyTable t1
  left join MyTable t2 on t1.C_ID=t2.C_ID
                      and t2.Date<=t1.Date
                      and t2.C_Rank='A'
group by t1.C_ID, t1.C_Rank, t1.Date
order by t1.C_ID, t1.Date

【讨论】:

谢谢 Chezy,你说得对,计数器会降低性能!!您的代码也可以与我的要求一起使用,并且执行时间更短。谢谢【参考方案4】:

以下代码满足要求:

create table #Temp_Table
(
C_ID int
, C_Rank char(1)
, Date datetime
, NewColumn int
)

insert into #Temp_Table
(
C_ID
, C_Rank
, Date
)
select 42, ‘A’, ’10/14/2010′
union all
select 42, ‘B’, ’10/26/2010′
union all
select 42, ‘B’, ’10/14/2010′
union all
select 42, ‘C’, ’10/26/2010′
union all
select 42, ‘A’,’02/16/2011′
union all
select 43, ‘A’, ’12/17/2010′
union all
select 44, ‘A’, ’07/28/2010′
union all
select 44, ‘B’, ’08/10/2010′
union all
select 44, ‘A’, ’01/11/2011′
union all
select 44, ‘B’, ’01/28/2011′
union all
select 44, ‘C’, ’10/14/2010′
union all
select 44, ‘D’, ’10/26/2010′

Select ‘Original Data’ Comment
,*
from #Temp_Table

/*
This would be Actual Script to get the New ID based on information you provided
*/
Declare @Count int
,@C_ID int
,@C_Rank char(1)
,@total_Count int
,@Count_Partition int
,@Previous_ID int 

Declare @Table Table (ID int IDENTITY(1,1), C_ID int, C_Rank char(1), Date datetime, NewColumn int )

Set @Count = 1
Set @Count_Partition = 0

insert into @Table
Select *
from #Temp_Table 

Select @total_Count = ISNULL(MAX(ID),0)
from @Table

While @Count < = @total_Count
Begin
Select @C_ID = C_ID
,@C_Rank = C_Rank
From @Table
Where ID = @Count

If @Count = 1
Set @Previous_ID = @C_ID

If @Previous_ID != @C_ID
Set @Count_Partition = 1

Else If @C_Rank = 'A'
Set @Count_Partition = @Count_Partition + 1

update @Table
Set NewColumn = @Count_Partition
Where ID = @Count

Set @Previous_ID = @C_ID
Set @Count = @Count + 1
End 

Select C_ID
, C_Rank
, [Date]
, NewColumn
from @Table

–Drop table #Temp_Table 

【讨论】:

我使用计数器来获取循环值。 在 SQL 中确实应该避免这样的循环。尝试在几千条记录上运行它,你就会明白我的意思了。

以上是关于使用其他列生成新列的主要内容,如果未能解决你的问题,请参考以下文章

使用其他现有列 Spark/Scala 添加新列

使用 UDF 从 Apache Spark 中的其他列创建新列

通过使用 hive 或 spark SQL 将一列作为参考来生成新列

pandas groupby 应用于多个列以生成新列

dplyr 创建一个具有其他列的复杂用户定义函数的新列

RedBean ORM ID 列