使用ODBC读取整个LOB的最佳方法是什么?

时间:2018-04-25 08:57:53

标签: mysql odbc mariadb

在整个LOB中读取你事先不知道的大小(没有最大分配+副本)应该是一个相当普遍的问题,但是以“正确”的方式找到良好的文档和/或示例已证明对我来说真是令人发狂。

我与SQLBindCol搏斗,但看不出任何有效的方法。 SQLDescribeColSQLColAttribute返回列元数据,这些元数据似乎是列大小的默认值或上限,而不是当前LOB的实际大小。最后,我决定使用以下内容:

1)将任何/所有LOB列作为SELECT语句中编号最高的列

2)SQLPrepare陈述

3)SQLBindCol您想要的任何早期非LOB列

4)SQLExecute陈述

5)SQLFetch结果行

6){L}列上的SQLGetData,其缓冲区大小为0,只是为了查询其实际大小

7)分配一个足够大的缓冲区以容纳你的LOB

8){L}列上的SQLGetData再次使用正确大小的分配缓冲区

9)对每个后来的LOB列重复步骤6-8

10)对结果集中的任何其他行重复步骤5-9

完成结果集后,

11)SQLCloseCursor

这似乎对我有用,但似乎也很有帮助。

对SQLGetData的调用是返回服务器还是仅处理已发送给客户端的结果?

服务器和/或客户端是否会拒绝以这种方式处理非常大的对象(例如 - 超出某个大小阈值,因此会产生错误)?

最重要的是,有更好的方法吗?

谢谢!

3 个答案:

答案 0 :(得分:2)

我看到要做的一些改进。

  1. 如果需要分配缓冲区,则应对所有记录和列执行一次。所以,您可以使用@RickJames建议的技术,使用这样的MAX进行改进:

    SELECT MAX(LENGTH(blob1))AS max1,MAX(LENGTH(blob2))AS max2,...

  2. 您可以使用max1max2预先分配缓冲区,也可以只使用所有列的最大缓冲区。

    1. 从1返回的缓冲区长度可能对于您的应用程序而言太大。您可以在运行时决定缓冲区的大小。无论如何,SQLGetData被设计为每列被调用多次。只需再次调用它,使用相同的列号,它将获取下一个块。可用字节数将保存在StrLen_or_IndPtr(最后一个参数)指向的位置。每次调用后,此计数将减少,并获取所获取的字节数。
    2. 当然,每次调用都会向服务器进行往返,因为所有这一切的目的都是为了防止驱动程序获取超出应用程序可以处理的内容。

      1. 在这种情况下禁止传递NULL作为缓冲区指针以获取长度的技巧,请在Microsoft的文档上查看SQLGetData
      2. 但是,您可以分配一个最小缓冲区,比如8个字节,传递它及其长度。该函数将返回写入的字节数,在我们的例子中为7,因为函数添加了一个空字符,并将StrLen_or_IndPtr的剩余字节数放在private ScaleGestureDetector mScaleDetector; private float zoom = 1.f; public MyCustomView(Context mContext){ ... // View code goes here ... mScaleDetector = new ScaleGestureDetector(context, new ScaleListener()); } @Override public boolean onTouchEvent(MotionEvent ev) { // Let the ScaleGestureDetector inspect all events. mScaleDetector.onTouchEvent(ev); return true; } @Override public void onDraw(Canvas canvas) { super.onDraw(canvas); canvas.save(); canvas.scale(zoom, zoom); ... // onDraw() code goes here ... canvas.restore(); } private class ScaleListener extends ScaleGestureDetector.SimpleOnScaleGestureListener { @Override public boolean onScale(ScaleGestureDetector detector) { mScaleFactor *= detector.getScaleFactor(); // Don't let the object get too small or too large. zoom = Math.max(0.1f, Math.min(mScaleFactor, 5.0f)); invalidate(); return true; } } 。但如果你按照上面的解释分配缓冲区,你可能不需要这样做。

        注意:LOB需要位于选择列表的末尾,并且必须按照该顺序精确提取。

答案 1 :(得分:1)

SQLGetData

SQLGetData获取已取结果的结果。例如,如果表格的第一行有SQLFetchSQLData会将第一行发回给您。如果您不知道是否可以SQLBindCol结果,则会使用它。

但它的处理方式取决于你的驱动程序,并没有在标准中描述。如果您的数据库是SQL数据库,则游标不能后退,因此结果可能仍在内存中。

大对象查询

服务器可能会拒绝根据服务器标准和您的 ODBC驱动程序标准处理大型对象。它没有在 ODBC标准中描述。

答案 2 :(得分:1)

要避免最大分配,进行额外复制,并提高效率:

首先获得大小并不是一个糟糕的方法 - 它几乎不需要额外的时间来完成

SELECT LENGTH(your_blob) FROM ...

然后进行分配并实际获取blob。

如果有多个BLOB列,请一次性获取所有长度:

SELECT LENGTH(blob1), LENGTH(blob2), ... FROM ...

在MySQL中,BLOBTEXT的长度可以在字节前面随时可用。但是,即使它必须读取列以获得长度,也可以将其视为仅仅启动缓存。也就是说,在任何一种情况下,整体时间都不会受到太大影响。