当前位置: 移动技术网 > IT编程>开发语言>PHP > PHP 数据库 常见问题小结第1/3页

PHP 数据库 常见问题小结第1/3页

2019年05月04日  | 移动技术网IT编程  | 我要评论

绷带包扎法,万宁政府网,神话放送e28


该代码不仅更短,而且也更容易理解和高效。我们不是执行两个查询,而是执行一个查询。
尽管该问题听起来有些牵强,但是在实践中我们通常总结出所有的表应该在同一个数据库中,除非有非常迫不得已的理由。
问题 4:不使用关系
关系数据库不同于编程语言,它们不具有数组类型。相反,它们使用表之间的关系来创建对象之间的一到多结构,这与数组具有相同的效果。我在应用程序中看到的一个问题是,工程师试图将数据库当作编程语言来使用,即通过使用具有逗号分隔的标识符的文本字符串来创建数组。请看下面的模式。
复制代码 代码如下:

drop table if exists files;
create table files (
id mediumint,
name text,
path text
);
drop table if exists users;
create table users (
id mediumint,
login text,
password text,
files text
);
insert into files values ( 1, 'test1.jpg', 'media/test1.jpg' );
insert into files values ( 2, 'test1.jpg', 'media/test1.jpg' );
insert into users values ( 1, 'jack', 'pass', '1,2' );

清单 10. bad.sql
系统中的一个用户可以具有多个文件。在编程语言中,应该使用数组来表示与一个用户相关联的文件。在本例中,程序员选择创建一个 files 字段,其中包含一个由逗号分隔的文件 id 列表。要得到一个特定用户的所有文件的列表,程序员必须首先从用户表中读取行,然后解析文件的文本,并为每个文件运行一个单独的 select 语句。该代码如下所示。
复制代码 代码如下:

<?php
require_once("db.php");
function get_files( $name )
{
$dsn = 'mysql://root:password@localhost/bad_norel';
$db =& db::connect( $dsn, array() );
if (pear::iserror($db)) { die($db->getmessage()); }
$res = $db->query( "select files from users where login=?",
array( $name ) );
$files = null;
while( $res->fetchinto( $row ) ) { $files = $row[0]; }
$rows = array();
foreach( split( ',',$files ) as $file )
{
$res = $db->query( "select * from files where id=?",
array( $file ) );
while( $res->fetchinto( $row ) ) { $rows[] = $row; }
}
return $rows;
}
$files = get_files( 'jack' );
var_dump( $files );
?>

清单 11. get.php
该技术很慢,难以维护,且没有很好地利用数据库。惟一的解决方案是重新架构模式,以将其转换回到传统的关系形式,如下所示。
复制代码 代码如下:

drop table if exists files;
create table files (
id mediumint,
user_id mediumint,
name text,
path text
);
drop table if exists users;
create table users (
id mediumint,
login text,
password text
);
insert into users values ( 1, 'jack', 'pass' );
insert into files values ( 1, 1, 'test1.jpg', 'media/test1.jpg' );
insert into files values ( 2, 1, 'test1.jpg', 'media/test1.jpg' );

清单 12. good.sql
这里,每个文件都通过 user_id 函数与文件表中的用户相关。这可能与任何将多个文件看成数组的人的思想相反。当然,数组不引用其包含的对象 —— 事实上,反之亦然。但是在关系数据库中,工作原理就是这样的,并且查询也因此要快速且简单得多。清单 13 展示了相应的 php 代码。
复制代码 代码如下:

<?php
require_once("db.php");
function get_files( $name )
{
$dsn = 'mysql://root:password@localhost/good_rel';
$db =& db::connect( $dsn, array() );
if (pear::iserror($db)) { die($db->getmessage()); }
$rows = array();
$res = $db->query(
"select files.* from users,files where users.login=?
and users.id=files.user_id",
array( $name ) );
while( $res->fetchinto( $row ) ) { $rows[] = $row; }
return $rows;
}
$files = get_files( 'jack' );
var_dump( $files );
?>

清单 13. get_good.php
这里,我们对数据库进行一次查询,以获得所有的行。代码不复杂,并且它将数据库作为其原有的用途使用。
问题 5:n+1 模式
我真不知有多少次看到过这样的大型应用程序,其中的代码首先检索一些实体(比如说客户),然后来回地一个一个地检索它们,以得到每个实体的详细信息。我们将其称为 n+1 模式,因为查询要执行这么多次 —— 一次查询检索所有实体的列表,然后对于 n 个实体中的每一个执行一次查询。当 n=10 时这还不成其为问题,但是当 n=100 或 n=1000 时呢?然后肯定会出现低效率问题。清单 14 展示了这种模式的一个例子。
复制代码 代码如下:

drop table if exists authors;
create table authors (
id mediumint not null auto_increment,
name text not null,
primary key ( id )
);
drop table if exists books;
create table books (
id mediumint not null auto_increment,
author_id mediumint not null,
name text not null,
primary key ( id )
);
insert into authors values ( null, 'jack herrington' );
insert into authors values ( null, 'dave thomas' );
insert into books values ( null, 1, 'code generation in action' );
insert into books values ( null, 1, 'podcasting hacks' );
insert into books values ( null, 1, 'php hacks' );
insert into books values ( null, 2, 'pragmatic programmer' );
insert into books values ( null, 2, 'ruby on rails' );
insert into books values ( null, 2, 'programming ruby' );
[code]
清单 14. schema.sql
该模式是可靠的,其中没有任何错误。问题在于访问数据库以找到一个给定作者的所有书籍的代码中,如下所示。
[code]
<?php
require_once('db.php');
$dsn = 'mysql://root:password@localhost/good_books';
$db =& db::connect( $dsn, array() );
if (pear::iserror($db)) { die($db->getmessage()); }
function get_author_id( $name )
{
global $db;
$res = $db->query( "select id from authors where name=?",
array( $name ) );
$id = null;
while( $res->fetchinto( $row ) ) { $id = $row[0]; }
return $id;
}
function get_books( $id )
{
global $db;
$res = $db->query( "select id from books where author_id=?",
array( $id ) );
$ids = array();
while( $res->fetchinto( $row ) ) { $ids []= $row[0]; }
return $ids;
}
function get_book( $id )
{
global $db;
$res = $db->query( "select * from books where id=?", array( $id ) );
while( $res->fetchinto( $row ) ) { return $row; }
return null;
}
$author_id = get_author_id( 'jack herrington' );
$books = get_books( $author_id );
foreach( $books as $book_id ) {
$book = get_book( $book_id );
var_dump( $book );
}
?>

清单 15. get.php
如果您看看下面的代码,您可能会想,“嘿,这才是真正的清楚明了。” 首先,得到作者 id,然后得到书籍列表,然后得到有关每本书的信息。的确,它很清楚明了,但是其高效吗?回答是否定的。看看只是检索 jack herrington 的书籍时要执行多少次查询。一次获得 id,另一次获得书籍列表,然后每本书执行一次查询。三本书要执行五次查询!
解决方案是用一个函数来执行大量的查询,如下所示。
复制代码 代码如下:

<?php
require_once('db.php');
$dsn = 'mysql://root:password@localhost/good_books';
$db =& db::connect( $dsn, array() );
if (pear::iserror($db)) { die($db->getmessage()); }
function get_books( $name )
{
global $db;
$res = $db->query(
"select books.* from authors,books where
books.author_id=authors.id and authors.name=?",
array( $name ) );
$rows = array();
while( $res->fetchinto( $row ) ) { $rows []= $row; }
return $rows;
}
$books = get_books( 'jack herrington' );
var_dump( $books );
?>

清单 16. get_good.php
现在检索列表需要一个快速、单个的查询。这意味着我将很可能必须具有几个这些类型的具有不同参数的方法,但是实在是没有选择。如果您想要具有一个扩展的 php 应用程序,那么必须有效地使用数据库,这意味着更智能的查询。

本例的问题是它有点太清晰了。通常来说,这些类型的 n+1 或 n*n 问题要微妙得多。并且它们只有在数据库管理员在系统具有性能问题时在系统上运行查询剖析器时才会出现。

结束语

数据库是强大的工具,就跟所有强大的工具一样,如果您不知道如何正确地使用就会滥用它们。识别和解决这些问题的诀窍是更好地理解底层技术。长期以来,我老听到业务逻辑编写人员抱怨,他们不想要必须理解数据库或 sql 代码。他们把数据库当成对象使用,并疑惑性能为什么如此之差。

他们没有认识到,理解 sql 对于将数据库从一个困难的必需品转换成强大的联盟是多么重要。如果您每天使用数据库,但是不熟悉 sql,那么请阅读 the art of sql,这本书写得很好,实践性也很强,可以指导您基本了解数据库。

3

如对本文有疑问,请在下面进行留言讨论,广大热心网友会与你互动!! 点击进行留言回复

相关文章:

验证码:
移动技术网