【问题标题】:Break up the data in a database column of a record into multiple records将记录的数据库列中的数据分解为多条记录
【发布时间】:2018-09-08 21:38:10
【问题描述】:

Azure SQL Server - 我们有一个这样的表:

我的表

ID    Source     ArticleText
--    ------     -----------
1     100        <nvarchar(max) field with unstructured text from media articles>
2     145        "
3     866        "
4     232        "

ID 列是INSERTS 的主键和自动增量。

我运行此查询以查找 ArticleText 列中数据量最大的记录:

SELECT TOP 500 
    ID, Source, DATALENGTH(ArticleText)/1048576 AS Size_in_MB
FROM 
    MyTable
ORDER BY 
    DATALENGTH(ArticleText) DESC

我们发现,由于技术和实际原因,ArticleText 列中的数据在某些记录中太大了。上面的查询允许我查看我们最大记录的一系列大小,我需要知道我在这里试图制定的内容。

我需要完成的壮举是,对于此表中的所有现有记录,任何ArticleText DATALENGTH 大于 X 的记录,将该记录分成 X 条记录,然后每条记录将包含相同的值在Source 列中,但将ArticleText 列中的数据拆分为较小的块中的这些记录。

如果确切的要求是这样的话,如何实现这一点,获取所有 ArticleText DATALENGTH 大于 10MB 的记录,并将每个记录分成 3 条记录,其中结果记录的 Source 列值在整个3 条记录,但 ArticleText 数据分为三个块。

本质上,我们需要将DATALENGTH 除以 3,并将文本数据的前 1/3 应用于第一条记录,将 2nd 1/3 应用于第二条记录,将 3rd 1/3 应用于第三条记录。

这在 SQL Server 中是否可行?

【问题讨论】:

  • 所以你不在乎打破单词边界?您是否打算添加一列来存储 ArticleText 段的顺序,还是不关心以任何特定顺序重新组装它们?
  • @HABO 这是关于打破工作界限的一个很好的观点。我们不会关心句子被拆分,但我们当然不希望任何单词被拆分。结果记录的顺序无关紧要。
  • 尝试从递归 CTE 开始,该 CTE 使用 CharIndex() 在某个起始位置后查找空格,例如CharIndex( ' ', ArticleText, 9999990 ),在空白处拆分字符串并重复直到字符串被消耗。它并不完美,因为它不识别标点符号、其他空格……,但这是一个开始。 (并且不要让那些讨厌的角色的三分之一让你夜不能寐。)

标签: sql-server tsql


【解决方案1】:

您可以使用以下代码创建包含所需数据的边表:

CREATE TABLE #mockup (ID INT IDENTITY, [Source] INT, ArticleText NVARCHAR(MAX));

INSERT INTO #mockup([Source],ArticleText) VALUES
 (100,'This is a very long text with many many words and it is still longer and longer and longer, and even longer and longer and longer')
,(200,'A short text')
,(300,'A medium text, just long enough to need a second part');

DECLARE @partSize INT=50;

WITH recCTE AS
(
    SELECT ID,[Source]
          ,1 AS FragmentIndex
          ,A.Pos
          ,CASE WHEN A.Pos>0 THEN LEFT(ArticleText,A.Pos) ELSE ArticleText END AS Fragment
          ,CASE WHEN A.Pos>0 THEN SUBSTRING(ArticleText,A.Pos+2,DATALENGTH(ArticleText)/2) END AS RestString
    FROM #mockup
    CROSS APPLY(SELECT CASE WHEN DATALENGTH(ArticleText)/2 > @partSize 
                            THEN @partSize - CHARINDEX(' ',REVERSE(LEFT(ArticleText,@partSize)))
                            ELSE -1 END AS Pos) A

    UNION ALL
    SELECT r.ID,r.[Source]
         ,r.FragmentIndex+1
         ,A.Pos
         ,CASE WHEN A.Pos>0 THEN LEFT(r.RestString,A.Pos) ELSE r.RestString END
         ,CASE WHEN A.Pos>0 THEN SUBSTRING(r.RestString,A.Pos+2,DATALENGTH(r.RestString)/2) END AS RestString
    FROM recCTE r
    CROSS APPLY(SELECT CASE WHEN DATALENGTH(r.RestString)/2 > @partSize 
                            THEN @partSize - CHARINDEX(' ',REVERSE(LEFT(r.RestString,@partSize)))
                            ELSE -1 END AS Pos) A
    WHERE DATALENGTH(r.RestString)>0
)
SELECT ID,[Source],FragmentIndex,Fragment 
FROM recCTE
ORDER BY [Source],FragmentIndex;

GO
DROP TABLE #mockup

结果

+----+--------+---------------+---------------------------------------------------+
| ID | Source | FragmentIndex | Fragment                                          |
+----+--------+---------------+---------------------------------------------------+
| 1  | 100    | 1             | This is a very long text with many many words and |
+----+--------+---------------+---------------------------------------------------+
| 1  | 100    | 2             | it is still longer and longer and longer, and     |
+----+--------+---------------+---------------------------------------------------+
| 1  | 100    | 3             | even longer and longer and longer                 |
+----+--------+---------------+---------------------------------------------------+
| 2  | 200    | 1             | A short text                                      |
+----+--------+---------------+---------------------------------------------------+
| 3  | 300    | 1             | A medium text, just long enough to need a second  |
+----+--------+---------------+---------------------------------------------------+
| 3  | 300    | 2             | part                                              |
+----+--------+---------------+---------------------------------------------------+

现在您必须使用FragmentIndex=1 的值更新现有行,同时您必须插入FragmentIndex&gt;1 的值。按 FragmentIndex 排序,您的IDENTITY ID 列将反映正确的顺序。

【讨论】:

  • 明天将尝试这个并回复任何问题/问题。谢谢Shnugo!
猜你喜欢
  • 2021-07-30
  • 2018-08-06
  • 1970-01-01
  • 2018-01-17
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多