在甲骨文LOB和PHP工作 
由哈里Fuecks

达到4,000字节的限制?输入LOB ...

在这个“Oracle + PHP Cookbook”HowTo中,您将学习可用的LOB类型和与之相关的问题,然后探索PHP中常见的LOB操作的示例。使用像VARCHAR2这样的Oracle类型是很好的,但是如果你需要能够一次存储超过4,000字节的限制呢?对于此任务,您需要一个Oracle的长对象(LOB)类型,这反过来要求您了解如何使用PHP API来处理LOB。这本身对于那些不熟悉它的人来说是令人生畏的。

Oracle中的长对象

Oracle提供以下LOB类型:

  • BLOB,用于存储二进制数据
  • CLOB,用于存储使用数据库字符集编码的字符数据
  • NCLOB,用于存储使用国家字符集的Unicode字符数据。需要注意的是NCLOBs是,目前不被支持PHP OCI8扩展,你会在这里使用。
  • BFILE,用于引用操作系统文件系统下的外部文件

LOB的另一个子类是临时LOB,它可以是BLOB,CLOB或NCLOB,但存储在临时表空间中,直到您释放它。

请注意,旧版本的Oracle分别为字符和二进制数据提供了LONG和LONG RAW类型。随着Oracle9需要这些赞成的LOB被弃用。

LOB存储。对于BLOB,CLOB和NCLOB类型,Oracle数据库10 ğ能够在一个单一的值存储高达128TB,这取决于你的数据库块大小和“块”设置,为LOB定义的。

LOB本身包括两个元素:LOB内容和LOB定位符,其是到LOB内容的“指针”。这种分离需要允许Oracle存储和有效地管理LOB和它反映在使用到PHP的API INSERT, 数据库,和 选择的LOB(见下文)。

对于内部LOB类型(即不是BFILE),如果LOB的大小小于4KB,则Oracle将在表中存储LOB的内容以及行的其余部分。大于4KB的LOB在默认情况下在表的表空间中存储为“out-of-line”。此方法允许快速检索小LOB,而对于大LOB,访问时间将较慢,但是在扫描表时的整体性能被保留。

还有LOB存储和访问的其他选项,例如内存缓存和缓冲 - 这可以提高性能,具体取决于您的应用程序的具体情况。欲了解更多信息,请参阅 LOB性能指南和 Oracle数据库应用程序开发人员指南-大型对象在Oracle文档中获得。

LOB的限制。许多限制适用于使用LOB类型,最重要的是它们在SQL语句中的使用。您不能在以下任何查询中使用LOB类型。

SELECT DISTINCT <lob_type>
ORDER BY <lob_type>
GROUP BY <lob_col>

这也是非法的表连接,使用LOB类型列 UNION, INTERSECTION和 MINUS语句。

其他限制适用于LOB使用的其他方面,例如,您不能将LOB用作主键列。再次,看 Oracle数据库应用程序开发人员指南-大型对象的详细信息。

CLOB和字符集

数据库的默认字符集由参数NLS_CHARACTERSET定义,并且放置在CLOB中的文本应使用此字符集进行编码。使用此SQL来确定您的数据库字符集编码:

SELECT value FROM nls_database_parameters WHERE parameter ='NLS_CHARACTERSET'

由于缺乏对PHP中NCLOB的支持,您可能需要考虑使用Unicode编码作为数据库字符,例如UTF-8,可以使用以下语句完成(给予足够的权限):

ALTER数据库字符集UTF8

注意:不要在不理解影响的情况下尝试此操作,尤其是如果现有数据或应用程序代码使用不同的字符集。看到 甲骨文全球化支持指南对全球化的Oracle PHP应用程序概述了解更多信息。

使用LOB

这里的讨论将集中在PHP的OCI8扩展。还值得注意的是,Oracle提供了DBMS_LOB包,其中包含使用PL / SQL处理LOB的并行过程和函数。

PHP OCI8扩展在全局PHP命名空间中注册了一个名为“OCI-Lob”的PHP类。当您执行 SELECT语句,例如,其中一列是一个LOB类型,PHP将自动绑定这个到OCI-LOB对象实例。一旦你有一个OCI高球对象的引用,你可以调用方法一样 的load()和 save()方法来访问或修改LOB的内容。

可用的OCI高球的方法将取决于你的PHP版本,PHP5特别是具有像获得方法 的read() , 求()和 追加() 。在 PHP手册是有点不清楚,在这种情况下,版本号,所以如果有疑问,您可以验证使用下面的脚本。

<?php
foreach(get_class_methods('OCI-Lob')as $ method){
print“OCI-Lob :: $ method()\ n”;
}}
?>

在我的系统上,运行PHP 5.0.5,我得到以下列表的方法:

OCI-Lob :: load()
OCI-Lob :: tell()
OCI-Lob :: truncate()
OCI-Lob :: erase()
OCI-Lob :: flush()
OCI-Lob :: setbuffering()
OCI-Lob :: getbuffering()
OCI-Lob :: rewind()
OCI-Lob :: read()
OCI-Lob :: eof()
OCI-Lob :: seek()
OCI-Lob :: write()
OCI-Lob :: append()
OCI-Lob :: size()
OCI-Lob :: writetofile()
OCI-Lob :: writetemporary()
OCI-Lob :: close()
OCI-Lob :: save()
OCI-Lob :: savefile()
OCI-Lob :: free()

在实践中,PHP 4.x OCI8扩展仅支持读取或写入完整的LOB,这是Web应用程序中最常见的用例。PHP5扩展是为了让LOB的“块”的阅读和写作,以及支持LOB缓冲的方法 setBuffering()和 getBuffering() 。PHP5还提供了独立的功能 oci_lob_is_equal()和 oci_lob_copy() 

此处的示例将使用新的PHP5 OCI函数名(如 oci_parse而不是 OCIParse)。示例使用以下序列和表:

CREATE SEQUENCE mylobs_id_seq
NOMINVALUE
NOMAXVALUE
NOCYCLE
CACHE 20
NOORDER
增量1; CREATE TABLE mylobs(
id NUMBER PRIMARY KEY,
mylob CLOB

注意,这里的大多数示例使用CLOB,但是相同的逻辑也可以几乎完全应用于BLOB。

插入LOB

要 INSERT内部LOB,首先需要使用相应的Oracle初始化LOB EMPTY_BLOB或 EMPTY_CLOB功能,您无法更新一个包含NULL值的LOB。

初始化后,你再绑定列到PHP OCI-LOB对象和更新通过对象的LOB的内容 保存()方法。

下面的脚本提供了一个示例,返回从该LOB类型 INSERT查询:

<?php
//连接到DB等... $ sql =“INSERT INTO
mylobs
((
ID,
mylob


((
mylobs_id_seq.NEXTVAL,
- 初始化为空CLOB
EMPTY_CLOB()

返回
- 返回LOB定位器
mylob INTO:mylob_loc“; $ stmt = oci_parse($ conn,$ sql); //创建一个“空”OCI-Lob对象以绑定到定位器
$ myLOB = oci_new_descriptor($ conn,OCI_D_LOB); //将返回的Oracle LOB定位符绑定到PHP LOB对象
oci_bind_by_name($ stmt,“:mylob_loc”,$ myLOB,-1,OCI_B_CLOB); //使用OCI_DEFAULT作为事务执行语句
oci_execute($ stmt,OCI_DEFAULT)
或死亡(“无法执行查询\ n”); //现在保存一个值到LOB
if(!$ myLOB-> save('INSERT:'.date('H:i:s',time()))){ //出错时,回滚事务
oci_rollback($ conn); } else { //成功后,提交事务
oci_commit($ conn); }} //免费资源
oci_free_statement($ stmt);
$ myLOB-> free(); //断开与DB的连接等。
?>

请注意这个例子中如何使用事务,指示 oci_execute与 OCI_DEFAULT常量等待 oci_commit或 oci_rollback。这一点很重要,因为我有两个阶段发生在 INSERT -首先创建行和第二次更新的LOB。

需要注意的是,如果我是用BLOB类型,唯一需要的变化(假设BLOB列)的工作是对 oci_bind_by_name调用:

oci_bind_by_name($ stmt,“:mylob_loc”,$ myLOB,-1,OCI_B_BLOB);

或者,您可以直接绑定字符串,而不指定LOB类型;

<?php
//等等。 $ sql =“INSERT INTO
mylobs
((
ID,
mylob


((
mylobs_id_seq.NEXTVAL,
:串

“; $ stmt = oci_parse($ conn,$ sql); oci_bind_by_name($ stmt,':string',$ string); oci_execute($ stmt)
或死亡(“无法执行查询\ n”); //等等。
?>

这种方法大大简化了代码,适合当你想要写入到LOB的数据相对较小。相反,如果你想大文件的内容流成LOB,你可以通过文件的内容循环调用 write()方法和 冲洗()的PHP LOB对象写较小的块上,而不是整个文件在单个实例中保持在内存中。

选择LOB

当 SELECT查询包含一个LOB列,PHP将自动绑定列到OCI-LOB对象。例如:

<?php
//等等。 $ sql =“SELECT
*:

mylobs
ORDER BY
ID
“; $ stmt = oci_parse($ conn,$ sql); oci_execute($ stmt)
或死亡(“无法执行查询\ n”); while($ row = oci_fetch_assoc($ stmt)){
print“ID:{$ row ['ID']},”; //调用load()方法来获取LOB的内容
print $ row ['MYLOB'] - > load()。“\ n”;
}} //等等。
?>

这可以通过使用进一步简化 OCI_RETURN_LOBS恒定,与所用 oci_fetch_array() ,指示它替换为它们的值LOB对象:

while($ row = oci_fetch_array($ stmt,OCI_ASSOC + OCI_RETURN_LOBS)){
print“ID:{$ row ['ID']},{$ row ['MYLOB']} \ n”;
}}

更新LOB

要 UPDATE一个LOB,它也可以使用 “回归”中的SQL命令,与上面 的INSERT的例子,但一个更简单的方法是 SELECT ... FOR UPDATE

<?php
//等等。 $ sql =“SELECT
mylob

mylobs
哪里
id = 3
FOR UPDATE / *锁定行* /
“; $ stmt = oci_parse($ conn,$ sql); //使用OCI_DEFAULT执行语句(开始事务)
oci_execute($ stmt,OCI_DEFAULT)
或死亡(“无法执行查询\ n”); //获取SELECTed行
if(FALSE ===($ row = oci_fetch_assoc($ stmt))){
oci_rollback($ conn);
die(“无法获取行\ n”);
}} //丢弃现有的LOB内容
if(!$ row ['MYLOB'] - > truncate()){
oci_rollback($ conn);
die(“Failed to truncate LOB \ n”);
}} //现在保存一个值到LOB
if(!$ row ['MYLOB'] - > save('UPDATE:'.date('H:i:s',time()))){ //出错时,回滚事务
oci_rollback($ conn); } else { //成功后,提交事务
oci_commit($ conn); }} //免费资源
oci_free_statement($ stmt);
$ row ['MYLOB'] - > free(); //等等。
?>

如同 INSERT,我需要执行 UPDATE使用事务。一个重要的附加步骤是将呼叫 截断() 。当更新与一个LOB 保存() ,将取代的LOB的从启动开始新的数据的长度的内容。这意味着较旧的内容(如果它比新的内容更长)可能仍然留在LOB中。

对于PHP 4.x中,在 截断()是不可用的,下面的替代解决方案使用Oracle的 EMPTY_CLOB()函数的新数据保存到前擦除在LOB任何现有内容。

$ sql =“UPDATE
mylobs

mylob = EMPTY_CLOB()
哪里
id = 2403
返回
mylob INTO:mylob
“; $ stmt = OCIParse($ conn,$ sql); $ mylob = OCINewDescriptor($ conn,OCI_D_LOB); OCIBindByName($ stmt,':mylob',$ mylob,-1,OCI_B_CLOB); //使用OCI_DEFAULT执行语句(开始事务)
OCIExecute($ stmt,OCI_DEFAULT)
或死亡(“无法执行查询\ n”); if(!$ mylob-> save('UPDATE:'.date('H:i:s',time()))){ OCIRollback($ conn);
die(“无法更新lob \ n”); }} OCICommit($ conn);
$ mylob-> free();
OCIFreeStatement($ stmt);

使用BFILES

当使用BFILE类型, INSERT S和 UPDATE刻薄告诉甲骨文在该文件所在的数据库服务器(可能不是同一台机器作为Web服务器)的文件系统中,而不是传递文件内容。使用 SELECT语句,您可以通过Oracle读取BFILE的内容,应该你愿意的话,或致电函数和过程从DBMS_LOB包,以获取有关文件的信息。

BFILE的主要优点是能够直接从文件系统访问原始文件,同时仍然能够使用SQL查找文件。这意味着,例如,图像可以直接由Web服务器提供,而我可以跟踪包含BFILES的表格和“用户”表之间的关系,告诉我谁上传了文件。

例如,我首先需要更新上面使用的表模式;

ALTER TABLE mylobs ADD(mybfile BFILE)

接下来,我需要向Oracle注册目录别名(这需要管理权限),并授予读取它的权限:

CREATE DIRECTORY IMAGES_DIR AS'/ home / harryf / public_html / images'

我现在可以 INSERT像一些BFILE的名称:

<?php
//等等。 //为BFILE名称构建一个INSERT
$ sql =“INSERT INTO
mylobs
((
ID,
mybfile


((
mylobs_id_seq.NEXTVAL,
/ *
使用Oracle目录引用传递文件名
我创建了名为IMAGES_DIR
* /
BFILENAME('IMAGES_DIR',:filename)
)“; $ stmt = oci_parse($ conn,$ sql); //打开目录
$ dir ='/ home / harryf / public_html / images';
$ dh = opendir($ dir)
或死亡(“无法打开$ dir”); //循环遍历目录的内容
while(false!==($ entry = readdir($ dh))){ //仅匹配扩展名为.jpg,.gif或.png的文件
if(is_file($ dir。'/'。$ entry)&& preg_match('/ \。(jpg | gif | png)$ /',$ entry) //绑定语句的文件名
oci_bind_by_name($ stmt,“:filename”,$ entry); //执行语句
if(oci_execute($ stmt)){
print“$ entry added \ n”;
}}
}} }}

如果我需要,我可以通过Oracle读取BFILE内容使用与上面相同的方法,我选择CLOB。或者,如果我需要获得文件名后面,所以我可以从文件系统中直接访问它们,我可以调用 DBMS_LOB.FILEGETNAME程序,如:

<?php
//等等。 $ sql =“SELECT
ID

mylobs
哪里
- 只选择不为空的BFILES
mybfile IS NOT NULL; $ stmt1 = oci_parse($ conn,$ sql); oci_execute($ stmt1)
或死亡(“无法执行查询\ n”); $ sql =“DECLARE
定位器BFILE;
diralias VARCHAR2(30);
filename VARCHAR2(30); 开始 选择
mybfile INTO locator

mylobs
哪里
id =:id; - 从BFILE获取文件名
DBMS_LOB.FILEGETNAME(locator,diralias,filename); - 分配OUT参数以绑定参数
:diralias:= diralias;
:filename:= filename; 结束;”; $ stmt2 = oci_parse($ conn,$ sql); oci_bind_by_name($ stmt2,“:id”,$ row ['ID']);
oci_bind_by_name($ stmt2,“:diralias”,$ diralias,30);
oci_bind_by_name($ stmt2,“:filename”,$ filename,30); oci_execute($ stmt2);
print“{$ row ['ID']}:$ diralias / $ filename \ n”; }}
//等等。
?>

此外,还可以使用 DBMS_LOB.FILEEXISTS功能来发现哪些文件已通过操作系统被删除,但在数据库中仍引用。

结论

在此方法文档中,你已经被介绍给不同类型的Oracle数据库10个可用的LOB ,希望现在了解他们在允许大型数据实体高效地存储在数据库中的作用。您还学习了如何使用PHP的OCI8 API来处理LOB,涵盖了在使用Oracle和PHP开发时遇到的常见用例。


哈利Fuecks [ http://www.phppatterns.com ]是一个知名的PHP开发人员和作家,自1999年发现PHP他发表了许多介绍,并通过中间PHP文章 Sitepoint网站开发者的网络,以及写作 的PHP文集(SitePoint)。

原文地址:http://www.oracle.com/technetwork/articles/fuecks-lobs-095315.html

【转】Oracle + PHP Cookbook(php oracle clob 长度超过4000如何写入)的更多相关文章

  1. clob字段超过4000转String类型

    上次提到listagg()和wm_concat()方法合并过的字段类型为clob,要是字段长度超过4000,直接使用to_char()方法转会报错. 解决方法可以在java代码中使用流的方式转化成字符 ...

  2. php/oracle: 解析oracle表中的NCLOB,CLOB字段里面的内容

    php/oracle: 解析oracle表中的NCLOB,CLOB字段里面的内容 假如你的字段名是:passenger_info 字段类型是:NCLOB/CLOB,在读表的时候,需要将 passeng ...

  3. 根据 oracle 标准计算超长字符串的长度

    Oracle 数据库使用 sql语句 :    select lengthb('输入字符串') from dual  ,  来计算 字符串 所占的字节长度(比如,一个汉字3个字节),但是用这个leng ...

  4. 转 Oracle全文检索http://docs.oracle.com/cd/E11882_01/text.112/e24436/toc.htm

    SQL > exec ctx_ddl.create_preference ('my_test_lexer','chinese_lexer') : PL/SQL 过程成功完成 SQL > E ...

  5. JAVA-Unit02: Oracle字符串操作 、 Oracle数值操作 、 Oracle日期操作 、 空值操作

    Unit02: Oracle字符串操作 . Oracle数值操作 . Oracle日期操作 . 空值操作 DQL数据查询语言 查询语句基本由SELECT子句由FROM子句构成. SELECT子句指定要 ...

  6. Oracle 11g XE 与 Oracle SQL Developer 的配置与使用(重制版)

    Oracle 11g XE 与 Oracle SQL Developer 的配置与使用(重制版) 前提概要 项目上需求要适应Oracle数据库,当然这和某EF框架也有关. 因为Oracle 的表名和列 ...

  7. 吴裕雄--天生自然 oracle学习笔记:oracle理论学习详解及各种简单操作例子

    1. 数据库的发展过程 层次模型 -->网状模型 -->关系模型 -->对象关系模型 2. 关于数据库的概念 DB:数据库(存储信息的仓库) DBMS:数据库管理系统(用于管理数据库 ...

  8. Oracle缓存表与Oracle缓存的区别

    一.Oracle缓存表 与 Oracle缓存 的概念 Oracle 缓存:是把Oracle近期查询的语句放置在Oracle设定的缓存当中. Oracle 缓存表:是把某个表放置在缓存当中,缓存是Ora ...

  9. Oracle 集群】ORACLE DATABASE 11G RAC 知识图文详细教程之ORACLE集群概念和原理(二)

    ORACLE集群概念和原理(二) 概述:写下本文档的初衷和动力,来源于上篇的<oracle基本操作手册>.oracle基本操作手册是作者研一假期对oracle基础知识学习的汇总.然后形成体 ...

随机推荐

  1. vs10创建sqlclr部署失败

    将项目解决方案改为3.5,调试OK:

  2. DDD(领域驱动设计)应对具体业务场景,如何聚焦 Domain Model(领域模型)?

    DDD(领域驱动设计)应对具体业务场景,如何聚焦 Domain Model(领域模型)? 阅读目录: 问题根源是什么? <领域驱动设计-软件核心复杂性应对之道>分层概念 Repositor ...

  3. 随机函数Surprising

    之前写了个用来抽取1-54号的随机函数,发现30-40出现的情况很大,就在果壳上提问了一下//听取了某个大神的建议循环了10000次之后惊喜的发现这样写出现了一大堆相同的数字! 之后有个很神大牛解答了 ...

  4. iOS关于应用内分享

    iOS7.0增加了AirDrop功能,可知在未来,手机信息资源的直接分享会越来越重要.而我们在iPhone系统短信点击照片会看到右上角的分享按钮,点击可以弹出一系列的应用菜单,允许用户把这张图片分享到 ...

  5. Select * 一定不走索引是否正确?

    Select * 一定不走索引是否正确? 走索引指的是:SQL语句的执行计划用到了1.聚集索引查找  2.索引查找  ,并且查询语句中需要有where子句 根据where子句的过滤条件,去聚集索引或非 ...

  6. 搜索广告与广告网络Demand技术-搜索广告

    搜索广告 搜索广告就是一个典型的Ad Network,但是搜索广告非常重要,它的收入非常高,所以它有其独特之处,复杂度也比展示广告要高.它与展示广告在点击率预测,检索部分差不多,它的特点:1. 用户定 ...

  7. WP8开发札记(一)WP8应用生命周期管理

    在介绍生命周期前,我们先了解两个相关的概念. 1.墓碑机制:WP8与Android采用的真后台机制不同,WP8采用的是墓碑机制.一旦从当前应用程序离开(非退出),该应用会被墓碑化,这样可以更好的管理( ...

  8. Java笔记:内部类

    1.把一个类放在另一类内部定义,这个定义在其他类里面的类就叫做内部类,包含内部类的类叫做外部类.内部类成员可以直接访问外部类的私有数据,但是外部类不能访问内部类的实现细节. 2.非静态内部类(没有st ...

  9. MSSQL Rebuild(重建)索引

    MSSQL Rebuild(重建)索引 前的项目是做数据库的归档,在每次archive后都需要对原数据库的索引进行rebuild,以减少索引碎片,于是乎就自己写了一段sql: DECLARE @tab ...

  10. OpenCV3.0.0+win10 64位+vs2015环境的下载,安装,配置

    操作系统:WIN10 pro 64 软件版本:VS2015+OpenCV3.0.0   1. 下载安装 http://opencv.org/ https://www.visualstudio.com/ ...