【问题标题】:RE-creating a 2D double array from a byte[] array stored in SQL从存储在 SQL 中的 byte[] 数组重新创建二维双精度数组
【发布时间】:2019-04-26 21:35:07
【问题描述】:

我有许多非常大的矩阵(有时是双精度型,但也可能是其他类型),它们的维度可能不同。我想将整个数组存储为 varbinary(max)

我能够很好地存储记录(我认为)。但是,当我来读取数据时,我可以创建一个具有多个维度的新双精度数组。我可以从 SQL 中读取存储的数据(我认为)。我现在想不通的是我使用的 Buffer.BlockCopy() 操作的逆操作。

SQL 表如下所示:

create table test_varbinary_table(
    id int not null IDENTITY PRIMARY KEY ,
    name varchar(256) not null,
    rows int not null,
    cols int not null,
    vb_data varbinary(max) null
)

写入数据的存储过程在这里: (删除,因为它可能只是混淆了这个问题) 编写示例记录的 C# 在这里: (删除是因为我觉得这部分有效,不知道对解决问题有帮助)

GetData 存储过程是这样的:

ALTER PROCEDURE [dbo].[GetData] 
AS
BEGIN
    -- SET NOCOUNT ON added to prevent extra result sets from
    -- interfering with SELECT statements.
    SET NOCOUNT ON;

    -- Insert statements for procedure here
    SELECT * from test_varbinary_table
END

我用

读表
    try
    {
        using (SqlConnection conn = new SqlConnection(connString))
        {
            conn.Open();
            SqlDataReader rdrMatrix = null;
            DataTable tblMatrix = new DataTable();

            using (SqlCommand cmd = new SqlCommand("dbo.GetData", conn))
            {
                cmd.CommandType = CommandType.StoredProcedure;
                rdrMatrix = cmd.ExecuteReader(CommandBehavior.CloseConnection);
                tblMatrix.Load(rdrMatrix);
                String strResult = "";
                foreach (DataRow dr in tblMatrix.Rows)
                {
                    int r = 2, c = 3;
                    double[,] dary = new double[r, c];
                    byte[] retData = new byte[6 * sizeof(double)];
                    String retName;
                    retName = dr["name"].ToString();
                    strResult += retName + "\r\n";
                    retData = (byte[])dr["vb_data"];  // potential problem?
                    Buffer.BlockCopy(retData, 0, dary, 0, dary.Length); // potential problem?

                    for (int ri = 0; ri< 2; ri++)
                    {
                        strResult += "     ";
                        for (int ci = 0; ci<3; ci++)
                        {
                            strResult += " " + dary[ri,ci] ;
                        }
                        strResult += "\r\n";
                    }

                    strResult +=  "\r\n" ;
                }
                textBox1.Text = strResult;
            }
            conn.Close();
        }
        //textBox1.Text = "No Failure!";
    }

对于写代码,我只是手动修改了内容,写了几条不同的记录。当我在 SSMS 中检查表时,我可以看到它已成功添加行。虽然我无法读取字节流,但如果我更改数据,它确实会改变,如果我不更改数据,它会保持不变。但是,当我运行 c# 代码来读取记录时,每次都会得到相同的结果......零。

输出如下:

foo
0 0 0
0 0 0
bar
0 0 0
0 0 0

所以不知何故,它要么没有读取@data 字段,要么没有正确地将其转换为 byte[],或者块复制无法正常工作。 (我认为。)无论如何,它返回的是零而不是我存储的数据。

【问题讨论】:

  • retData的原始值吗?
  • 我正在努力解决这个问题。
  • 我找到了解决方案。我还在玩它。如果我记得,我会在几天内写一些东西。我无法让 Buffer.BlockCopy 取消编码,即使这是我用来编码为字节数组的方法。相反,我使用 BlockCopy 进行编码和 tmpArray[i] = BitConverter.ToDouble(retData, i * 8);用于解码 where double tmpArray[r*c].

标签: c# arrays tsql multidimensional-array double


【解决方案1】:

我能够使用 BlockCopy 将矩阵编码为可以写入 varbinary 的字节数组来解决问题。阅读时,我使用 BitConverter 将字节数组解码为 double[,]。我不知道为什么我不能让 BlockCopy 朝另一个方向工作。我错过了一些东西。

无论如何,到目前为止,我的解决方案使用了一些不同的代码,但这些是基本元素:

我存储矩阵的表:

CREATE TABLE [dbo].[test_varbinary_table](
    [id] [int] IDENTITY(1,1) NOT NULL,
    [name] [varchar](8) NOT NULL,
    [version] [varchar](16) NOT NULL,
    [date] [datetime] NOT NULL,
    [notes] [varchar](max) NULL,
    [rows] [int] NOT NULL,
    [cols] [int] NOT NULL,
    [checksum] [float] NULL,
    [vb_data] [varbinary](max) NULL
) 

将 double[,] 编码为字节数组的方法,该字节数组将直接写入 varbinary:

public byte[] DoubleMatrixToByteArray(double[,] doubleArray)
{
    int rows = doubleArray.GetLength(0);
    int cols = doubleArray.GetLength(1);
    byte[] byteArray = new byte[rows * cols * sizeof(double)];
    Buffer.BlockCopy(doubleArray, 0, byteArray, 0, byteArray.Length);
    return byteArray;
}

从 varbinary 解码回二维双精度数组的方法:

    public double[,] VarBinaryToDoubleMatrix (byte[] byteArray, int rows, int cols)
    {
        double[,] doubleArray = new double[rows, cols];
        for (int r=0; r<rows; r++)
        {
            for (int c=0; c<cols; c++)
            {
                doubleArray[r,c] = BitConverter.ToDouble(byteArray, r * cols * sizeof(double) + c * sizeof(double));
            }
        }
        return doubleArray;
    }

校验和:

// Summation of all cells in a matrix, just a quick verification check.
// The intent is to verify that when a matrix is read from the database,
// the reconstructed matrix has the same checksum as the one that was saved.
//
    public double computeChecksum(double[,] dblArray)
    {
        int rank = dblArray.Rank; // rank is the number of dimensions of an array.
        int rows = dblArray.GetLength(0);
        int cols = dblArray.GetLength(1);
        double sum = 0.0;

        for (int r = 0; r < rows; r++)
        {
            for (int c = 0; c < cols; c++)
            {
                sum += dblArray[r, c];
            }
        }

        return sum;
    }
}

我使用校验和保存数组,该校验和与 varbinary 字段一起读取。然后我重新计算校验和并将其与保存的校验和值进行比较。

【讨论】:

  • 在我看来这应该是对单个函数的库调用。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2011-08-05
  • 1970-01-01
  • 2012-11-10
相关资源
最近更新 更多