【问题标题】:How to invert words in a column如何反转列中的单词
【发布时间】:2009-04-16 05:35:31
【问题描述】:

我有一个带有 varchar(128) 列的表,其中包含如下数据:

word1, word2, , word3, word4
word1, word2, word3, ,
单词1,,,, ; word2
;word1 word2, word3

我要更新这张表,让单词倒序:

word4, ,word3,,word2 ,word1
,,word3, ,word2, word1

您能帮我只使用单个 sql 查询吗?

【问题讨论】:

  • 逗号和分号重要吗?
  • 这个问题需要澄清一下。
  • 单词之间的区别是什么?空间?逗号?分号?
  • 分隔符的数量是否固定?

标签: sql sql-server sql-server-2005 tsql


【解决方案1】:

要完成这个任务需要一个 t-sql 函数和一个游标。 fn_SplitList 将允许您基于分隔符进行拆分。拥有此功能后,您可以创建一个游标来针对更新每条记录的数据运行。我使用@table1 创建了一个示例。

功能

CREATE FUNCTION [dbo].[fn_SplitList]
(
    @RowData varchar(8000),
    @SplitOn varchar(5)
)  
RETURNS @RtnValue table 
(
    Id int identity(1,1),
    Data varchar(100)
) 
AS  
BEGIN 
    Declare @Cnt int
    Set @Cnt = 1

    While (Charindex(@SplitOn,@RowData)>0)
    Begin
        Insert Into @RtnValue (data)
        Select 
            Data = ltrim(rtrim(Substring(@RowData,1,Charindex(@SplitOn,@RowData)-1)))

        Set @RowData = Substring(@RowData,Charindex(@SplitOn,@RowData)+1,len(@RowData))
        Set @Cnt = @Cnt + 1
    End

    Insert Into @RtnValue (data)
    Select Data = ltrim(rtrim(@RowData))

    Return
END

执行更新的代码

declare @table1 table(id int primary key
                     ,words varchar(max))
declare @id int
declare @words varchar(max)

insert into @table1 values(0, 'word1, word2, , word3, word4')
insert into @table1 values(1, 'word1, word2, word3, ,')
insert into @table1 values(2, 'word1,,,, ; word2')
insert into @table1 values(3, ';word1 word2, word3')

declare updateCursor cursor for
select id
      ,words
  from @table1

open updateCursor   
fetch next from updateCursor into @id, @words
while @@fetch_status = 0
begin
  declare @row varchar(255)

  select @row = coalesce(@row+', ', '') + data
    from dbo.fn_SplitList(@words, ',')
  order by id desc

  update @table1
     set words = @row
   where id = @id

  fetch next from updateCursor into @id, @words
end
close updateCursor   
deallocate updateCursor

select *
  from @table1

【讨论】:

    【解决方案2】:

    最好的办法是永远不要以这种方式存储数据。如果这在应有的相关表格中,那么您可以按自己的意愿订购。就个人而言,至少将数据拆分为具有额外标识列的临时表或表变量,然后在该列上按降序排序可能是最快的。

    【讨论】:

      【解决方案3】:

      我认为您可以创建一个新表并将第一个表中的数据插入到该表中。

      INSERT INTO TABLE2 SELECT * FROM TABLE1 ORDER BY COLUMN1 DESC

      然后,删除 table2 并重命名 table1。

      也许……

      【讨论】:

        【解决方案4】:

        我有一个想法,因为您受限于一个查询。你能制作一个与我之前建议的临时表做同样事情的表值 UDF 吗?加入其中并订购 desc。它不会是高性能的(当然,当结构设计得如此糟糕时,什么都不会)但它可能会完成这项工作,除非你有数百万条记录。

        或者您可以创建一个存储过程,然后从您的 GUI 中调用它,这将允许您执行多个步骤。

        【讨论】:

          【解决方案5】:

          感谢您的回答

          单词由空格、逗号和/或分号分隔, 所有分隔符都应保持原位

          例如记录

          'word1,word2;;;word3,'

          评估为

          'word3, word2;;;word1, '

          由于现有系统的要求,必须这样做 使用单个查询,

          我试过了:

          update t_desc set name = 
          (select name
          from
          (select name,
          case when wb is null then rname when wb >= we then stuff(rname, wb, 128, REVERSE        (substring(rname, wb, 128))) else
          stuff(rname, wb, we-wb+1, REVERSE(substring(rname, wb, we-wb+1))) end rname,
          case when wb is null or wb > we then null else PATINDEX('%[a-z0-9]%', SUBSTRING(rname, we+1, 128))+we end wb,
          case when we is null or wb > we then null else PATINDEX('%[a-z0-9][^a-z0-9]%', SUBSTRING(rname, we+1, 128))+we end we
          from
          (select name,
          case when wb is null then rname when wb >= we then stuff(rname, wb, 128, REVERSE    (substring(rname, wb, 128))) else
          stuff(rname, wb, we-wb+1, REVERSE(substring(rname, wb, we-wb+1))) end rname,
          case when wb is null or wb > we then null else PATINDEX('%[a-z0-9]%', SUBSTRING    (rname, we+1, 128))+we end wb,
          case when we is null or wb > we then null else PATINDEX('%[a-z0-9][^a-z0-9]%',     SUBSTRING(rname, we+1, 128))+we end we
          from
          (select name,
          case when wb is null then rname when wb >= we then stuff(rname, wb, 128, REVERSE        (substring(rname, wb, 128))) else
          stuff(rname, wb, we-wb+1, REVERSE(substring(rname, wb, we-wb+1))) end rname,
          case when wb is null or wb > we then null else PATINDEX('%[a-z0-9]%', SUBSTRING        (rname, we+1, 128))+we end wb,
          case when we is null or wb > we then null else PATINDEX('%[a-z0-9][^a-z0-9]%',         SUBSTRING(rname, we+1, 128))+we end we
          from
          (select name, 
          stuff(rname, wb, we-wb+1, REVERSE(substring(rname, wb, we-wb+1))) rname,
          PATINDEX('%[a-z0-9]%', SUBSTRING(rname, we+1, 128))+we wb,
          PATINDEX('%[a-z0-9][^a-z0-9]%', SUBSTRING(rname, we+1, 128))+we we 
          from
          (select name, rname, 
          PATINDEX('%[a-z0-9]%', rname) wb,
          PATINDEX('%[a-z0-9][^a-z0-9]%', rname) we
            from
          (select t_desc.name, REVERSE(name) rname) t1) t2) t3) t4) t5) t6)
          

          和类似的想法,但它不能正常工作

          编辑: 除字母和数字外的所有内容都是分隔符

          编辑2: 不幸的是,我无法调用 ddl 语句,也无法直接访问数据库, 我的公司使用旧的、封闭的软件——我们只能使用外部应用程序的控制台来选择或有时更新。 我可以将我的更新添加到工作列表中 - 以后每天都会执行。

          表有大约 60k 行, 列包含 2 到大约 20 个单词,不为空

          对不起我的英语:)

          【讨论】:

            猜你喜欢
            • 1970-01-01
            • 2015-02-26
            • 1970-01-01
            • 2013-09-23
            • 2013-04-01
            • 2018-05-19
            • 1970-01-01
            • 1970-01-01
            • 2017-09-05
            相关资源
            最近更新 更多