【问题标题】:SQL Server read csv binary from tableSQL Server 从表中读取 csv 二进制文件
【发布时间】:2014-07-04 22:30:03
【问题描述】:

我目前将我的 csv 格式文件存储在磁盘上,然后像这样查询它们:

SELECT *
FROM OPENROWSET(BULK 'C:\myfile.csv',
    FORMATFILE = 'C\format.fmt',
    FIRSTROW = 2) AS rs

其中 format.fmt 是 csv 文件中列的定义格式。这很好用。 但我有兴趣将文件存储在 SQL Server 表中,而不是将它们存储在磁盘上。 因此,当具有 VARBINARY(MAX) 数据类型列时。如何查询它们?

如果我有这样的表:

CREATE TABLE FileTable
(
    [FileName] NVARCHAR(256)
    ,[File] VARBINARY(MAX)
)

一行'myfile.csv', '0x427574696B3B44616....'

例如,如何将该文件内容读入临时表?

【问题讨论】:

  • 如果您有 CSV 数据,为什么不直接将其导入数据库?
  • 我不完全关注。是的,我得到了 CSV 数据,因为用户上传了包含该数据的文件。因此,我可以将其存储在 NVARCHAR(MAX) 列中,而不是将数据存储为二进制文件:'value1;value2;value3\r\nvalue4;value5;value6\r\n'。这可能是一个简单的问题,但是如何在不编写大量自定义代码的情况下解析该列?
  • 如果您有 CSV 数据,那么将其放入数据库并直接使用它是一件简单的事情,而不是绕着房子试图使用文件中的数据。这里有一个指南:blog.sqlauthority.com/2011/05/12/…
  • 因为无论如何我都需要将它存储在数据库中,因为我需要备份原始数据,而对我来说最好的选择是将它存储在它所属的表中的数据库中,我的想法是将其存储在磁盘上只是为了能够将其导入另一张桌子是“绕着房子走”。这将临时保存到磁盘,导入它然后删除磁盘上的文件。如果我可以直接从它已经存在的列中导入它,对我来说似乎没有必要。这就是我问题的重点。我能否避免保存到磁盘并直接从表中读取它。
  • 说真的,除非您没有告诉我们有关您的设置的某些内容,否则您的看法完全错误。您可以非常轻松地将数据库中的文件作为数据库表进行备份。使用这种格式比尝试转换为 VARBINARY 并再次返回要容易得多。

标签: sql-server file csv blob


【解决方案1】:

如果您确实需要使用 varbinary 数据,您可以将其转换回 nvarchar:

DECLARE @bin VARBINARY(MAX)
SET @bin = 0x5468697320697320612074657374

SELECT CAST(@bin as VARCHAR(MAX))
-- gives This is a test

将其转换为该格式后,您可以使用拆分函数将其转换为表格。不要问我为什么 SQL Server 中没有内置的拆分函数,因为这是一个非常明显的疏忽,但实际上并没有。所以用下面的代码创建你自己的:

CREATE FUNCTION [dbo].[fn_splitDelimitedToTable] ( @delimiter varchar(3), @StringInput VARCHAR(8000) )
RETURNS @OutputTable TABLE ([String] VARCHAR(100), [Hierarchy] int )
AS
BEGIN

    DECLARE @String    VARCHAR(100)
    DECLARE @row int = 0

    WHILE LEN(@StringInput) > 0
    BEGIN
        SET @row = @row + 1
        SET @String      = LEFT(@StringInput, 
                                ISNULL(NULLIF(CHARINDEX(@delimiter, @StringInput) - 1, -1),
                                LEN(@StringInput)))
        SET @StringInput = SUBSTRING(@StringInput,
                                     ISNULL(NULLIF(CHARINDEX(@delimiter, @StringInput), 0),
                                     LEN(@StringInput)) + 1, LEN(@StringInput))

        INSERT INTO @OutputTable ( [String], [Hierarchy] )
        VALUES ( @String, @row )
    END

    RETURN
END

把它们放在一起:

select CAST('one,two,three' as VARBINARY)
-- gives 0x6F6E652C74776F2C7468726565

DECLARE @bin VARBINARY(MAX)
SET @bin = 0x6F6E652C74776F2C7468726565

select * from fn_splitDelimitedToTable(',', CAST(@bin as VARCHAR(MAX)))

给出这个结果:

string hierarchy
================
one    1
two    2
three  3

当然,如果您愿意,您可以将结果放入临时表中:

select * into #myTempTable
from fn_splitDelimitedToTable(',', CAST(@bin as VARCHAR(MAX)))

【讨论】:

  • 感谢您尝试解决问题。对多于一列和多行的情况不太好,但我可以查看并修复它。无论如何,我一直在寻找内置的东西。否则我的工作解决方案是可以接受的。很好奇我是否可以避免一些不必要的操作。谢谢。
  • 没问题。它应该可以处理列,但我可以看到行将是一个问题:您需要找出行分隔符并尝试使用这些分隔符。如果答案至少有帮助,请多多支持:)
【解决方案2】:

如果您有 CSV 数据,为什么不直接将其导入数据库?

您也可以使用 BULK INSERT 来执行此操作,如 this question

假设您已经创建了一个格式正确的表格以将数据导入(例如“MyImportTable”),则可以使用以下内容:

BULK INSERT MyImportTable
FROM 'C:\myfile.csv'
WITH (DATAFILETYPE='char',
      FIRSTROW = 2,
      FORMATFILE = 'C\format.fmt');

编辑 1:

将数据导入数据库后,您现在可以直接查询表,避免像这样完全使用 CSV:

 SELECT *
   FROM MyImportTable

不再需要对原始 CSV 的引用,您可以删除/归档原始 CSV。

编辑 2:

如果您启用了 xp_cmdshell,并且您拥有适当的权限,则可以使用以下命令从 SQL 中删除该文件:

xp_cmdshell 'del c:\myfile.csv'

最后,如果要启用 xp_cmdshell,请使用以下命令:

exec sp_configure
go
exec sp_configure 'xp_cmdshell', 1
go
reconfigure
go

【讨论】:

  • 这就是我目前正在做的事情。至少有点。我想改变它,因此问题......
  • 请参阅编辑:通过将数据导入数据库表,您不再需要存储/访问原始 CSV 文件。您当前的解决方案对 CSV 文件进行操作,并表格一样呈现数据。
  • 是的,那是因为我对将数据存储在表中不感兴趣,因为数据不会原始进入表中。它需要大量的操作和计算。我知道我可以稍后更新表格,但目前我计算出数据,完成后我插入它。但是是的,我需要存储原始文件。这始终是一个好习惯,因为当客户来并说他/她发送了那种类型的数据时,我可以通过查看原始文件来确认或看到情况并非如此。请阅读主帖上的 cmets。
  • 我不是在问如何批量插入文件。在过去的五年里,我一直知道如何做到这一点。如果您阅读了这个问题,我的意思是这就是我目前正在做的事情。还是谢谢。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2015-01-08
  • 2018-08-24
  • 2015-07-27
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多