`

[转]SQLite FAQ中文版

 
阅读更多

1) 如何创建自增字段?

简单的回答:一个声明为 INTEGER PRIMARY KEY 的字段将自动增加。

这里是详细的答案: 从 SQLite 的 2.3.4 版本开始,如果你将一个表中的一个字段声明为 INTEGER PRIMARY KEY,那么无论你何时向该表的该字段插入一个 NULL 值,这个 NULL 值将自动被更换为比表中该字段所有行的最大值大 1 的整数;如果表为空,那么将被更换为 1。比如,假设你有这样的一张数据表:

CREATE TABLE t1(
a INTEGER PRIMARY KEY,
b INTEGER
);

在这张数据表里,声明

INSERT INTO t1 VALUES(NULL,123);

在逻辑意义上等价于:

INSERT INTO t1 VALUES((SELECT max(a) FROM t1)+1,123);

一个新的API函数 sqlite3_last_insert_rowid() 返回最近的插入操作的整形键

注意这个整型键始终比之前插入表中的最后一个键大1。新键相对于表中的已有键来说是唯一的, 但它可能与之前从表中删除的键值重叠。要始终得到在整个表中唯一的键,在INTEGER PRIMARY KEY的声明之前加关键词AUTOINCREMENT.这样被选的键将总是比表中已存在的最大键大1。若可能的 最大键已存在于表中,INSERT操作将失败并返回一个SQLITE_FULL错误码.


(2) SQLite 支持哪些数据类型?

参见 http://www.sqlite.org/datatype3.html .


(3) 为什么能向 SQLite 数据库的整型字段中插入字符串?

这是一个功能,不是一个 bug。你可以在任何字段中放任何信息,而不用管字段声明为什么类型。 你可以往整型字段中插入任意长度的字符串,或者往布尔字段中插入浮点数,或者往字符字段中 插入日期。在 CREATE TABLE 命令中你指定给这个字段的数据类型不会限制插入这个字段的数据。 所有的字段可以插入任意长度的字符串。但对于 INTEGER PRIMARY KEY 字段例外。这种字段只能 存放一个64位的整数,否则会出错。

但SQLite会默认你希望使用声明的字段类型。所以,比如你希望在一个声明为INTEGER的字段 中插入一个字符串,SQLite会试图将其转换为一个整数。如果转换成功,那么整数将被插入,否 则插入字符串,这种特性有时被称作type or column affinity .


(4) 为什么 SQLite 认为表达式 '0'=='00' 为真?

在 2.7.0 之后,表达式不成立。参见文档 datatypes in SQLite version 3


(5) 为什么 SQLite 不允许在同一张表里使用 '0' 和 '0.0' 作为两个不同的行的主键?

你的主键一定是数值类型的,把类型改为 TEXT 就可以了。

每一行必须有一个唯一的主键。作为一个数字类型的字段,SQLite 认为 '0' 和 '0.0' 的值是相同的, 因为他们在数字上的比较是相等的(看前面的问题)因此值不是唯一的。


(6) 为什么不能在 Linux box 中读取在 SparcStation 中创建的 SQLite 数据库?

你需要升级你的 SQLite 库到 2.6.3 或更新版本。

x86 处理器是 little-endian 型的而 Sparc 是 big-endian 型的。新版本的 SQLite 解决了这个问题。

注:   big endian和little endian是CPU处理多字节数的不同方式。例如“汉”字的Unicode编码是6C49。那么写到文件里时,究竟是将6C写在前面,还是将49写在前面?如果将6C写在前面,就是big endian。还是将49写在前面,就是little endian。


(7) 多个应用程序或者同一个应用程序的多个例程能同时存取同一个数据库文件吗?

多进程可以同时打开同一个数据库,也可以同时 SELECT 。但只有一个进程可以立即改数据库。

SQLite使用读/写锁定来控制数据库访问。(Win95/98/ME 操作系统缺乏读/写锁定支持,在低于 2.7.0 的版本中,这意味着在 windows 下在同一时间内只能有一个进程读数据库。在版本 2.7.0 中 这个问题通过在 windows 接口代码中执行一个用户间隔几率读写锁定策略解决了。) 但如果数据库文件在一个 NFS 文件系统中,控制并发读书的锁定机制可以会出错。因为 NFS 的fcntl() 文件锁定有时会出问题。如果有多进程可能并发读数据库则因当避免把数据库文件放在 NFS 文件系统中。 根据微软的文档,如果不运行 Share.exe 后台程序则 FAT 文件系统中的锁定可能不工作。对 Windows 非常有经验的人告诉我网络文件的锁定有许多问题并且不可靠。如果是这样,在2个或以上 Windows 系统中共享一个 SQLite 数据库文件会导致不可预知的问题。

我们知道没有其他的嵌入式 SQL数据库引擎比SQLite支持更多的并发性。 SQLite允许多进程 同时打开和读取数据库。任何一个进程需要写入时,整个数据库将在这一过程中被锁定。但这一般仅耗时 几毫秒。其他进程只需等待然后继续其他事务。其他嵌入式SQL数据库引擎往往只允许单进程访问数据库。

但是,client/server型的数据库引擎 (如 PostgreSQL, MySQL, 以及 Oracle) 通常支持更高的并发度, 并支持多进程同时写入同一个数据库。由于总有一个控制良好的服务器协调数据库的访问,这才保证了以上 特性的实现。如果你的应用需要很高的并发度,你应该考虑使用client/server数据库。事实上,经验告诉 我们大多数应用所需要的并发度比他们的设计者们想象的要少得多。

当 SQLite 尝试操作一个被另一个进程锁定的文件时,缺省的行为是返回 SQLITE_BUSY。你可以用 C代码更改这一行为。 使用 sqlite3_busy_handler()sqlite3_busy_timeout() API函数。

如果两个或更多进程同时打开同一个数据库,其中一个进程创建了新的表或索引,则其它进程可能不能立即看见新的表。其它进程可能需要关闭并重新连结数据库。


(8) SQLite是线程安全的吗?

有时候是的。为了线程安全,SQLite 必须在编译时把 THREADSAFE 预处理宏设为1。在缺省的发行的已编译版本中 Windows 版的是线程安全的,而 Linux 版的不是。如果要求线程安全,Linux 版的要重新编译。

“线程安全”是指二个或三个线程可以同时调用独立的不同的sqlite3_open() 返回的"sqlite3 "结构。而不是在多线程中同时使用同一个 sqlite3 结构指针。

一个sqlite3 结构只能在调用 sqlite3_open 创建它的那个进程中使用。你不能在一个线程中打开一个数据库然后把指针传递给另一个线程使用。这是因为大多数多线程系统的限制(或 Bugs?)例如RedHat9上。在这些有问题的系统上,一个 线程创建的fcntl()锁不能由另一个线程删除或修改。由于SQLite依赖fcntl()锁来进行并发控制,当在线程间传递数据库连接时会出现严重的问题。

也许在Linux下有办法解决fcntl()锁的问题,但那十分复杂并且对于正确性的测试将是极度困难的。因此,SQLite目前不允许在线程间共享句柄。

在UNIX下,你不能通过一个 fork() 系统调用把一个打开的 SQLite 数据库放入子过程中,否则会出错。


(9) 如何列出一个 SQLite 数据库中的所有的表/索引?

sqlite3 命令行程序中你可以用命令 ".tables " 来显示所有的表或者用 ".schema "来显示所有的表结构和索引。但命令后不要跟 LIKE 语句,否则会限制表的显示。

在 C/C++ 程序中 (或使用 Tcl/Ruby/Perl/Python绑定的脚本中)你可以通过访问名为"SQLITE_MASTER 的表来实现。每个 SQLite 数据库有一个 SQLITE_MASTER 表,表内有数据库的结构。SQLITE_MASTER表是这样的:

CREATE TABLE sqlite_master (
type TEXT,
name TEXT,
tbl_name TEXT,
rootpage INTEGER,
sql TEXT
);

对于表来说,type 字段的值为'table' ,name 字段是表的名称。使用以下语句可以等到所有表的列表:

SELECT name FROM sqlite_master
WHERE type='table'
ORDER BY name;

对于索引来说, type = 'index' , name 是索引的名称, tbl_name 是索引所属的表的名称。对于表和索引,sql 字段是创建表或索引的原始语句文本。对于自动创建的索引(一般是使用 PRIMARY KEY 或 UNIQUE 创建的),sql 字段为 NULL.

SQLITE_MASTER表是只读的。你不能对该表使用 UPDATE, INSERT, 或 DELETE。该表自动由 CREATE TABLE, CREATE INDEX, DROP TABLE 和 DROP INDEX 命令更新。

临时表及其索引不在 SQLITE_MASTER 表中而在 SQLITE_TEMP_MASTER 中出现。SQLITE_TEMP_MASTER 与 SQLITE_MASTER 表一样工作,但只对于创建临时表的程序可见。要得到所在表包括临时表可以使用如下命令:

SELECT name FROM 
(SELECT * FROM sqlite_master UNION ALL
SELECT * FROM sqlite_temp_master)
WHERE type='table'
ORDER BY name


(10) SQLite数据库是否有已知的大小限制?

数据库大小被限制在 2TB(241 bytes). 这是理论限制。事实上,你应该把 SQLite数据库的大小限制在100GB以下,以免出现运行性能上的问题。如果你需要储存100GB或更多数据在一个数据库中, 考虑使用为此而设计的企业版数据库吧。

一个数据库的理论行数限制是 264 -1,显然你会在达到行数限制之前先超过文件大小的限制。目前一行可以存放 230 bytes 数据。而基本的文件格式可以支持行大小到约 262bytes.

可能还会有对于表、索引的数目或表和索引中的字段数的限制,但没人知道是多少。事实上,每当新数据库打开时,SQLite需要读取和 分析所有表和索引声明的初始SQL,所以,为了调用 sqlite3_open() 时获得最佳性能,最好减少声明的表的数目。同样的,即使 对于表中字段数没有限制,多于100个也显得太多了。 只有表开头的31个字段会得到优化。你可以在一个索引中放入任意多的字段但超过30字段的索引将不用于优化查询。

表,索引,视图,触发器和字段名称可以任意长,但SQL 函数名 (由sqlite3_create_function() API创建的)不得超过255个字符。


(11) 在 SQLite 中 VARCHAR 的最大长度是多少?

SQLite不强制VARCHAR的长度。你可以声明一个VARCHAR(10),SQLite一样可以让你存放500个字符在里面。 并且它们会始终完整无缺——决不会被截断。


(12) SQLite 是否支持 BLOB 类型?

SQLite 3.0 版支持在任何字段存放 BLOB 数据,不管字段声明为什么类型。


(13) 如何从一个已存在的 SQLite 数据表中添加/删除字段?

SQLite有有限的ALTER TABLE 支持,可以用于添加字段到表的末尾 或更改表名。如果你要对表的结构作更复杂的修改,你需要重新创建表。你可以在一个临时表中备份数据,撤销旧表,重建新表后再恢复数据。

例如,假设你有一个名为 "t1" 的表,有名为 "a", "b", 和 "c" 三个字段,你要删除字段 "c" 。可按如下步骤操作:

BEGIN TRANSACTION;
CREATE TEMPORARY TABLE t1_backup(a,b);
INSERT INTO t1_backup SELECT a,b FROM t1;
DROP TABLE t1;
CREATE TABLE t1(a,b);
INSERT INTO t1 SELECT a,b FROM t1_backup;
DROP TABLE t1_backup;
COMMIT;


(14) 我删除了很多数据但是数据库文件并没有减小,是不是 Bug?

不是的。当你从 SQLite 删除数据之后,未使用的磁盘空间被添加到一个内在的“空闲列表”中用于存储你下次插入的数据。磁盘空间并没有丢失,但是也不向操作系统返回磁盘空间。

如果你删除了大量的数据且想要减小数据库文件,执行 VACUUM 命令。VACUUM 命令会清空“空闲列表”,把数据库尺寸缩到最小。注意, VACUUM 会耗费一些时间(在 Linux 系统下大约0.5秒/兆)并且要使用两倍于数据库文件大小的磁盘空间。

对于SQLite version 3.1, 替代VACUUM命令的一个方法是auto-vacuum模式,用auto_vacuum pragma 语法开启该模式。


(15) 是否能将 SQLite 用于商业用途而不用交版权费用?

可以。SQLite 是公开的 。代码的任何部分都没有声明所有权。你可以用它来做你想要的任何事情。


(16) 如何插入有单引号(')的字符串?

使用双单引号即可,例如:

INSERT INTO xyz VALUES('5 O''clock');

插入数据库的是:5 0'clock。


(17) SQLITE_SCHEMA 错误代表什么?

在 SQLite 版本3中,当一个预处理 SQL 语句不合法不能执行时就会返回一个 SQLITE_SCHEMA 错误。当这个错误发生时,该语句应当用 sqlite3_prepare() API函数重新编译。在 SQLite 版本3中,只有使用 sqlite3_prepare() /sqlite3_step()/sqlite3_finalize() API函数执行 SQL 才会发生这个错误,而使用 sqlite3_exec() . 则不会。这与版本2不同。

大部分发生这个错误的原因是当 SQL 预处理完时数据库已经改变了(可能是被另一个进程改变的)。还可能有如下原因:

  • 对一个数据库进行DETACH 操作
  • 对一个数据库进行VACUUM 操作
  • 一个用户函数定义被删除或改变了。
  • 一个排序定义被删除或改变了。
  • 一个授权函数改变了。

解决的办法是重新编译并再次尝试执行。所有涉及 sqlite3_prepare() /sqlite3_step()/sqlite3_finalize() API 函数的都应当重新编译。参见下例:

int rc;
sqlite3_stmt *pStmt;
char zSql[] = "SELECT .....";

do {
/* Compile the statement from SQL. Assume success. */
sqlite3_prepare(pDb, zSql, -1, &pStmt, 0);

while( SQLITE_ROW==sqlite3_step(pStmt) ){
/* Do something with the row of available data */
}

/* Finalize the statement. If an SQLITE_SCHEMA error has
** occured, then the above call to sqlite3_step() will have
** returned SQLITE_ERROR. sqlite3_finalize() will return
** SQLITE_SCHEMA. In this case the loop will execute again.
*/
rc = sqlite3_finalize(pStmt);
} while( rc==SQLITE_SCHEMA );



(18) 为什么ROUND(9.95,1) 返回 9.9 而不是 10.0? 难道9.95 不该向上进位么?

SQLite 内部使用二进制运算,9.95用 64-bit IEEE 浮点数 ( SQLite 内部使用的) 表示为9.949999999999999289457264239899814128875732421875。所以当你输入 "9.95"时,SQLite 就理解为上述的数字,进而四舍五入得到9.9。这个问题在处理浮点二进制数总会产生。通常的规则是十进制的有限浮点数通常无法表示为二进制有限浮点数,只能由最接近的二进制数来代替。这个近似数会非常接近原数,但总一些细微的不同,所以可能无法得到你预期的结果

分享到:
评论
1 楼 w11h22j33 2011-12-22  
给sqlite数据库加密的两种方法

一个是采用SQLCipher

Need to store sensitive information in your app? SQLCipher  extends SQLite enabling transparent encryption and decryption of data using AES. Its source  is available on Github.

SQLite is pluggable. Developers can create extensions and chain them into SQLite’s engine. Using this mechanism, SQLCipher embeds itself low enough in the stack to be transparent. As a developer, you simply issue queries as you normally would and all of the crypto is handled transparently.



SQLCipher’s author, Stephen Lombardo of Zetetic , has also released Cryptographically Secure File I/O  a library that supports random access file IO on encrypted files.



另外一个

Strong Encryption for Cocoa  / Cocoa  Touch
AES  is a strong encryption standard that has mostly replaced the aging DES standard. AES is widely used and fairly secure encryption mechanism (but I am not an expert at cryptography by any stretch of the imagination; I’m trusting experts for that opinion). AES supports three different key sizes, 128, 192, and 256 (the larger the key, the more secure the encryption and the more processing power it takes to encrypt  or decrypt). Apple uses AES-128 and AES-256 in several places in Mac OS X, including for Disk Image encryption.


There are several public-domain implementations of AES. I chose a public domain implementation of AES by Philip J. Erdelsky  to use as the basis some Objective-C categories that make encrypting and decrypting files and data using AES-256 easy.

The first category is on NSFileManager , and allows you to encrypt  a file in the filesystem. It takes a file at a particular pathname, encrypts it using a passphrase, and then writes the encrypted contents to a new specified file location. This version has relatively low memory overhead, as it streams the data in chunks both for reading and writing, so only the chunk currently being encrypted is in memory. The category adds two methods to NSFileManager , one for encrypting, the other for decrypting. These methods are the best choice when your source data already exists in the file system, especially on the iPhone, because of how little memory it uses to do the work. Here is an example of using the category on NSFileManager  to encrypt  a file:

NSError *error = nil ;
if (![ [ NSFileManager defaultManager ] AESEncryptFile: @" /path/to/input file" toFile: @" /path/to/output file" usingPassphrase: @" My secret password" error: &error ] )
{
NSLog (@" Failed to write encrypted file. Error = %@" , [ [ error userInfo ] objectForKey: AESEncryptionErrorDescriptionKey ] );
}
There is also a category on NSData  that will let you encrypt  a chunk of data that’s already in memory. This version creates a new NSData  object with the encrypted contents of the original NSData  instance. If your data is already in memory, and you want an encrypted or decrypted version of it, then the NSData methods are the way to go. Here is an example of using encrypting an NSData object with AES:

NSData *encryptedData = [ data AESEncryptWithPassphrase: @" My secret password" ] ;
Pretty easy, huh? Okay, now, this is a symmetric block cypher, it is not public-key encryption, so if you store your passphrase as a string in your application (as opposed to making the user enter it or storing it in the keychain) then you’re giving somebody the ability to decrypt your encrypted application data, so just be forewarned.

Also, I make no warranties about how secure this is. As far as I know, AES-256 has not been broken yet, however I cannot say for certaint that there are no weaknesses in the AES implementation I’ve used. I don’t see any obvious problems but I am not a cryptographer. I haven’t heard of any weaknesses in this particular implementation, but any use of this is completely at your own risk.

Here is a zip file containing the two categories and the AES implementation . Just add these to your Xcode project, include the appropriate headers, andencrypt  away.

Oh, and, one more important thing:  If you use this in an iPhone application that you plan to sell on the App Store, it may lengthen the review process, as you will have to declare that you are using encryption, and will likely have to create and upload a CCATS  form and wait for Apple to review it before your app will go up for sale. Read the iTunes Connect Developer Guide for more information on CCATS and the process before deciding to use this in an iPhone application for sale, please.

UPDATE:  Jim Dovey  author of the terrific Output iPhone App , posted a category on NSMutableData  in the comments to this post that uses the crypto libraries already available on the Mac and iPhone to do AES-256 encryption. According to Jim, this means you don’t need a CCATS form because Apple’s exporting the encryption code, not you, so check it out Thanks, Jim.

相关推荐

    SqLite的常见问题列表。(很有用的哦)

    本文严格整理自最新的:http://www.sqlite.org/faq.html, (3.7.8) (多说一句,看一个系统的发布,经常首先要看的就是,readme, 新特性,然后就是FAQ列表, 即所谓的常见问题列表)

    SQLite Viewer-crx插件

    语言:English 支持打开多个数据库 功能的表格sqlite查看器和编辑器:1....请访问FAQ页面以获取更多信息。您可以使用此扩展程序查看SQLite查询甚至创建新数据库或修改现有数据库。您可以更改并保存结果以供以后使用。

    SQLite 中文指南之FAQ第1/6页

    sqllite使用过程中碰到的一些问题解决,中文版

    phpMyFAQ 开源问答系统 v3.0.0 beta2.zip

    phpMyFAQ是一个支持多语言的,用php技术开发的开源FAQ(问答)系统,类似百度知道,支持MySQL/SQLite等多种数据库。phpMyFAQ具有内容管理功能,图片管理,支持多用户,用户组、新闻系统、用户跟踪、语言模块,支持...

    phpMyFAQ v2.9.10 多国语言版的FAQ系统

    MySQL, PostgreSQL, SQLite, Sybase, MS SQL Server, IBM DB2, IBM Cloudscape, Apache Derby, Oracle, Interbase, or Firebird. 内容管理系统 我们可以进行用户,用户组、新闻、分类、FAQ记录、密码等多方面的管理...

    phpMyFAQ 问答系统 v2.9.11.rar

    phpMyFAQ具有内容管理功能,图片管理,支持多用户,用户组、新闻系统、用户跟踪、语言模块,支持 Microsoft Active Directry 活动目录,支持MySQL, PostgreSQL, SQLite, Sybase, MS SQL Server, IBM DB2, IBM ...

    phpMyFAQ 开源问答系统 v2.9.0 alpha

    •更新捆绑Symfony的类加载器2.5.2版 •更新捆绑了jQuery1.11.1版本 •更新捆绑引导到3.2.0版本 •更新捆绑字体真棒至4.0.3版本 •更新捆绑Modernizr的到2.7.2版本 •更新捆绑的TinyMCE到4.0.28版本 •掉线转...

    phpMyFAQ 开源问答系统 v3.1.0 beta

    phpMyFAQ是一个支持多语言的,用php技术开发的开源FAQ(问答)系统,类似百度知道,支持MySQL/SQLite等多种数据库。phpMyFAQ具有内容管理功能,图片管理,支持多用户,用户组、新闻系统、用户跟踪、语言模块,支持...

    phpMyFAQ v2.9.13 多国语言版

     phpMyFAQ具体有如下特性:支持多种数据库MySQL, PostgreSQL, SQLite, Sybase, MS SQL Server, IBM DB2, IBM Cloudscape, ApacheDerby, Oracle, Interbase, or Firebird.内容管理系统我们可以进行用户,用户组、...

    phpMyFAQ v2.9.11 多国语言版源码

    MySQL, PostgreSQL, SQLite, Sybase, MS SQL Server, IBM DB2, IBM Cloudscape, Apache Derby, Oracle, Interbase, or Firebird. 内容管理系统 我们可以进行用户,用户组、新闻、分类、FAQ记录、密码等多方面的管理...

    phpMyFAQ v2.9.13 多国语言版.zip

    MySQL, PostgreSQL, SQLite, Sybase, MS SQL Server, IBM DB2, IBM Cloudscape, Apache Derby, Oracle, Interbase, or Firebir 内容管理系统 我们可以进行用户,用户组、新闻、分类、FAQ记录、密码等多方面的管理...

    phpMyFAQ v2.6.17 多国语言版.rar

    MySQL, PostgreSQL, SQLite, Sybase, MS SQL Server, IBM DB2, IBM Cloudscape, Apache Derby, Oracle, Interbase, or Firebird. 内容管理系统 我们可以进行用户,用户组、新闻、分类、FAQ记录、密码等多方面的管理...

    PHP官方手册中文版

    PHP官方手册中文版 PHP 手册 Mehdi Achour Friedhelm Betz Antony Dovgal Nuno Lopes Philip Olson Georg Richter Damien Seguy Jakub Vrana 编辑 Gabor Hojtsy 目录 前言 作者和贡献者 I. 入门...

    PHP手册2007整合中文版

    FAQ:常见问题 59. 一般信息 60. 邮件列表 61. 获取 PHP 62. 数据库问题 63. 安装常见问题 64. 编译问题 65. 使用 PHP 66. PHP 和 HTML 67. PHP 和 COM 68. PHP 和其它语言 69. 从 PHP/FI 2 移植到 PHP 3 70. 从 ...

    phpMyFAQ:phpMyFAQ-适用于PHP和MySQL,PostgreSQL和其他数据库的开源FAQ Web应用程序

    phpMyFAQ是一个多语言,完全由数据库驱动的FAQ系统。 它支持各种数据库来存储所有数据,需要PHP 7.3+才能访问此数据。 phpMyFAQ还提供了带有所见即所得编辑器和图像管理器的多语言内容管理系统,Elasticsearch的...

    中文版PHP使用手册

    FAQ:常见问题 47. 一般信息 48. 邮件列表 49. 获取 PHP 50. 数据库问题 51. 安装常见问题 52. 编译问题 53. 使用 PHP 54. PHP 和 HTML 55. PHP 和 COM 56. PHP 和其它语言 57. 从 PHP/FI 2 移植到 PHP 3 58. 从 ...

    phpMyFAQ v2.8.13 多国语言版

    •回迁的sqlite3的全力支持 •更新中国(繁体)翻译 •更新波斯语翻译 •更新意大利语翻译 •更新的西班牙语翻译 •修正了一些小错误 phpMyFAQ是一个支持多语言的FAQ系统,类似百度知道,支持多种数据库。...

    android开发资料大全

    《Android中文教程》中文版 《android基础教程合集》 Android实例教程 会员贡献索引贴 实用Android开发工具和资源精选 APK权限大全 - Android必懂知识 最无私的Android资料(书籍+代码)分享[总结] Android中文...

    phpMyFAQ 3.0.0 beta2.zip

    phpMyFAQ是一个支持多语言,用php技术开发的开源FAQ(问答)系统,类似百度知道,支持MySQL/SQLite等多种数据库。phpMyFAQ具有内容管理功能,图片管理,支持多用户,用户组、新闻系统、用户跟踪、语言模块,支持...

    phpMyFAQ v3.0.6 多国语言版

    支持多种数据库MySQL, PostgreSQL, SQLite, Sybase, MS SQL Server, IBM DB2, IBM Cloudscape, ApacheDerby, Oracle, Interbase, or Firebir内容管理系统我们可以进行用户,用户组、新闻、分类、FAQ记录、密码等多...

Global site tag (gtag.js) - Google Analytics