Get Adobe Flash player

MySQL 資料型態

資料庫就是要來存放各種類型的資料,因此一般程式語言所支援的資料型態,資料庫都會支援,

MySQL中支援的資料型態有:1. 整數型別 2. 小數型別 3. 字元型別 4. 日期型別 Read the rest of this entry »

MySQL 初體驗

前兩天看了 SQLite 相關的用法後,會了一些很基礎的 SQL 語法,
在此也稍微用了那些很基本的 SQL 語法在 MySQL 上面進行操作,
MySQL一樣也是有提供一個 mysql 指令,讓我們可以連入 MySQL shell,
且在 MySQL shell 中,一樣可以讓我們下達管理指令還有SQL語法,

—————

幾個觀念:

1. 在 MySQL 中,一個資料庫就是一個目錄,一個資料表就是一個檔案。
(在 Linux 中,MySQL 的家目錄在 /var/lib/mysql )
2. MySQL 的設定檔在 /etc/my.cnf (若有修改的話,要重新啟動 MySQL)
3. SQL語法是不分大小寫的 (CASE_INSENSITIVE),不過我們知道資料表是一個檔案,
資料表名稱即為該檔案名稱,在Linux的系統中是大小寫有別的 (CASE_SENSITIVE),
因此在下SQL語法時,資料表與資料庫名稱的大小寫都要匹配才行 (當然啦資料庫名稱也一樣)。
4. 良好的 SQL 語法習慣,資料表名稱、欄位 以及 資料庫名稱都用小寫,只有 SQL 語法用大寫。
5. 把SQL唸成"西括"是一種錯誤的唸法,就唸 "S" "Q" "L" 就好;
不過還是看跟你溝通的對象吧,唸 "西括" 也沒差,像 192.168.x.x 叫作 private(私有) IP,
很多人還是會稱它虛擬IP,就入境隨俗吧!

—————————

Example:

建立MySQL資料庫:(兩種方法)

1. 進入 MySQL shell 執行以下命令:

// 首先介紹一下 mysql 的參數
//   -h 後面接的是你的 mysql server
//   -u 後面接的是要登入的帳號 -u root 也可以寫成連在一起的 -uroot
//   -p 就是你的登入密碼啦,也可以直接打在後面,怕人家看到的話可以不打,之後提示你輸入密碼(有*號)
[lego@lego-HA-centos6-1 ~]$ mysql -h localhost -u root -p
Enter password:
Welcome to the MySQL monitor.  Commands end with ; or \g.
Your MySQL connection id is 4
Server version: 5.1.61 Source distribution

Copyright (c) 2000, 2011, Oracle and/or its affiliates. All rights reserved.

Oracle is a registered trademark of Oracle Corporation and/or its
affiliates. Other names may be trademarks of their respective
owners.

Type 'help;' or '\h' for help. Type '\c' to clear the current input statement.

// 建立資料庫

mysql> CREATE DATABASE legodb;
Query OK, 1 row affected (0.00 sec)
// 更好的建立資料庫方式,應該要先判斷一下該資料庫是否已經存在 (就跟我們寫程式一樣,要使用某變數前先判斷是否為NULL)
mysql> CREATE DATABASE IF NOT EXISTS legodb;
Query OK, 1 row affected, 1 warning (0.00 sec)

// 檢查資料庫是否已新增,使用 show databases; 管理指令
mysql> show databases;
+--------------------+
| Database           |
+--------------------+
| information_schema |
| card               |
| legodb             |
| mysql              |
+--------------------+
4 rows in set (0.00 sec)

2. 直接在 /var/lib/mysql/ 目錄下,建立一個目錄: mkdir legodb2 (記得要把該目錄的擁有者與群組改成mysql)。

// 建立資料表

資料表一定屬於某一個資料庫,因此在建立時,我們需要讓 MySQL shell 知道我們要在哪個資料庫建立資料表。

1. 直接在建立資料表前加上資料庫的名稱:

// 在 legodb 下,建立一個 users 的資料表,其 Schema 為 id, name, age, sex
mysql> CREATE TABLE legodb.users(id int, name char(10), age int, sex char(3));
Query OK, 0 rows affected (0.01 sec)

// 顯示 legodb 下的所有資料表
mysql> show tables from legodb;
+------------------+
| Tables_in_legodb |
+------------------+
| users            |
+------------------+
1 row in set (0.00 sec)

2. 先聲明要使用哪個資料庫,往後建立資料表時,MySQL shell就會知道是建立該資料庫的資料表,
當然你依然可以在資料表前加上資料庫名的名稱。

// 使用 legodb 這個 Database
mysql> use legodb;
Reading table information for completion of table and column names
You can turn off this feature to get a quicker startup with -A

Database changed

// 建立 users2 資料表
mysql> CREATE TABLE users2(id int, name char(10), age int, sex char(3));
Query OK, 0 rows affected (0.00 sec)

// 建立資料表時,與建立資料庫一樣,判斷一下是否已存在,是個好習慣
mysql> CREATE TABLE IF NOT EXISTS users2(id int, name char(10), age int, sex char(3));
Query OK, 0 rows affected, 1 warning (0.00 sec)

// 資料表操作:新增(INSERT)、刪除(DELETE OR DROP)、修改(UPDATE)、查詢(SELECT)

// 首先查看一下,你要插入的表結構
mysql> desc users;
+-------+----------+------+-----+---------+-------+
| Field | Type     | Null | Key | Default | Extra |
+-------+----------+------+-----+---------+-------+
| id    | int(11)  | YES  |     | NULL    |       |
| name  | char(10) | YES  |     | NULL    |       |
| age   | int(11)  | YES  |     | NULL    |       |
| sex   | char(3)  | YES  |     | NULL    |       |
+-------+----------+------+-----+---------+-------+
4 rows in set (0.00 sec)

// 插入一筆資料,在此我們按照欄位的順序插入,當然我們也可以在插入時,指定欄位順序
mysql> INSERT INTO users VALUES(1, 'ptrancer', 20, 'b');
Query OK, 1 row affected (0.00 sec)

// 所有的輸入值,我們都加上 ' 號的話, MySQL 會自動幫我們轉型,這也是個避免出錯的使用習慣。
mysql> INSERT INTO users VALUES('2', 'kenis', '26', 'b');
Query OK, 1 row affected (0.00 sec)

// 如何指令欄位插入的順序呢 ? (其實不管你有沒有要調整插入時的欄位順序,都應該使用這種方法,看起來很清楚)
mysql> INSERT INTO users(name, age, sex, id) VALUES('sweet', '12', 'g', '5');
Query OK, 1 row affected (0.00 sec)

// 是否可以指插入某幾個欄位的值,其他不管 ?
mysql> INSERT INTO users(id, name) VALUES('10', 'tony');
Query OK, 1 row affected (0.00 sec)

// 查詢 users 表格的內容! (驗證結果)
mysql> SELECT * FROM users;
+------+----------+------+------+
| id   | name     | age  | sex  |
+------+----------+------+------+
|    1 | ptrancer |   20 | b    |
|    2 | kenis    |   26 | b    |
|    5 | sweet    |   12 | g    |
|   10 | tony     | NULL | NULL |
+------+----------+------+------+
4 rows in set (0.00 sec)

// 修改資料表內容 (UPDATE)
mysql> UPDATE users SET age='30', sex='g' WHERE name='ptrancer';
Query OK, 1 row affected (0.00 sec)
Rows matched: 1  Changed: 1  Warnings: 0

// 查詢 users 資料表的內容! (驗證結果)
mysql> SELECT * FROM users;
+------+----------+------+------+
| id   | name     | age  | sex  |
+------+----------+------+------+
|    1 | ptrancer |   30 | g    |
|    2 | kenis    |   26 | b    |
|    5 | sweet    |   12 | g    |
+------+----------+------+------+
3 rows in set (0.00 sec)

// 刪除一筆資料 (千萬別寫成 delete from users ,這樣會把整個 users資料表清空)
mysql> DELETE FROM users WHERE name='tony';
Query OK, 1 row affected (0.00 sec)

// 查詢 users 資料表的內容! (驗證結果)
mysql> SELECT * FROM users;
+------+----------+------+------+
| id   | name     | age  | sex  |
+------+----------+------+------+
|    1 | ptrancer |   20 | b    |
|    2 | kenis    |   26 | b    |
|    5 | sweet    |   12 | g    |
+------+----------+------+------+
3 rows in set (0.00 sec)

// 刪除剛剛無聊建立的 users2 資料表,刪除資料庫也一樣使用 drop 語法 (ex: DROP DATABASE legodb)。
mysql> DROP TABLE users2;
Query OK, 0 rows affected (0.00 sec)

// 當然比較好的刪除方式,還是得先判斷一下該資料表或資料庫是否存在
mysql> DROP TABLE IF EXISTS users2;
Query OK, 0 rows affected (0.00 sec)

經由上述的語法說明,基本的 SQL 語法也差不多走過一次了,
接下來備忘一些 MySQL 提供的管理指令:

1. 查看 MySQL 的狀態,可輸入 \s 或 status

mysql> status
--------------
mysql  Ver 14.14 Distrib 5.1.61, for redhat-linux-gnu (i386) using readline 5.1

Connection id:          10
Current database:       legodb
Current user:           root@localhost
SSL:                    Not in use
Current pager:          stdout
Using outfile:          ''
Using delimiter:        ;
Server version:         5.1.61 Source distribution
Protocol version:       10
Connection:             Localhost via UNIX socket
Server characterset:    latin1
Db     characterset:    latin1
Client characterset:    latin1
Conn.  characterset:    latin1
UNIX socket:            /var/lib/mysql/mysql.sock
Uptime:                 12 hours 38 min 16 sec

Threads: 1  Questions: 184  Slow queries: 0  Opens: 39  Flush tables: 1  Open ta
bles: 25  Queries per second avg: 0.4
--------------

2. 顯示 MySQL 配置的環境變數 (可在/etc/my.cnf中配置)

mysql> show variables;

// 內容非常多,所以不在此佔版面了
// 我們也可以指定要看某一個環境變數的內容

mysql> show variables like 'time_zone';
+---------------+--------+
| Variable_name | Value  |
+---------------+--------+
| time_zone     | SYSTEM |
+---------------+--------+
1 row in set (0.00 sec)

接下來這個指令的使用,我覺得非常重要(五顆星),也就是 helper 功能,
當我忘了 SQL 語法或者 MySQL 的管理指令時,可以使用 ? contents 或 help contents 都一樣可以查。

mysql> ? contents;
You asked for help about help category: "Contents"
For more information, type 'help <item>', where <item> is one of the following
categories:
   Account Management
   Administration
   Compound Statements
   Data Definition
   Data Manipulation
   Data Types
   Functions
   Functions and Modifiers for Use with GROUP BY
   Geographic Features
   Help Metadata
   Language Structure
   Plugins
   Table Maintenance
   Transactions
   User-Defined Functions
   Utility

// 由上面的顯示中,我們來查一下 MySQL 中有支援哪些資料格式好了 (Data Types)
mysql> ? data types;
You asked for help about help category: "Data Types"
For more information, type 'help <item>', where <item> is one of the following
topics:
   AUTO_INCREMENT
   BIGINT
   BINARY
   BIT
   BLOB
   BLOB DATA TYPE
   BOOLEAN
   CHAR
   CHAR BYTE
   DATE
   DATETIME
   DEC
   DECIMAL
   DOUBLE
   DOUBLE PRECISION
   ENUM
   FLOAT
   INT
   INTEGER
   LONGBLOB
   LONGTEXT
   MEDIUMBLOB
   MEDIUMINT
   MEDIUMTEXT
   SET DATA TYPE
   SMALLINT
   TEXT
   TIME
   TIMESTAMP
   TINYBLOB
   TINYINT
   TINYTEXT
   VARBINARY
   VARCHAR
   YEAR DATA TYPE

// 忘了 CREATE TABLE 語法怎麼用
mysql> ? CREATE TABLE;
// 會跳出一堆用法,與示例

用 MySQL 提供的 helper 好處就是不用上網,且好查也很詳細。
初體驗就先此吧!!

————————————————–

其實 MySQL 賣給 ORACLE 之後,我就一直挺擔心它的發展性,
不曉得此技術是否還值得投資,不過在爬文之後,看到了 MariaDB,就比較安心了。
因為 MariaDB 完全相容於 MySQL,且管理指令與SQL語法也一樣,
當然我相信還是會有差異之處,只要變化不大就好。
官網:http://mariadb.org/

目前關於 MariaDB 的書擊只有這本:

http://ptgmedia.pearsoncmg.com/images/9780321799944/samplepages/0321799941.pdf

SQLite : Use C to access SQLite

在此備忘一下,如何使用 C 語言存取 SQLite,
以下的程式碼,是一個可以讓我們輸入 SQL 語法,
來對 SQLite Database 進行新增,查詢,修改,刪除資料表內容與新增資料表的程式。

#include <stdio.h>
#include <stdlib.h>
#include <sqlite3.h>

static int callback(void *NotUsed, int argc, char **argv, char **azColName){
  int i;
  for (i=0; i<argc; i++) {
    printf("%s = %s\n", azColName[i], argv[i] ? argv[i] : "NULL");
  }
  printf("\n");
  return 0;
}

int main(int argc, char** argv) {
  sqlite3 *db;
  char *zErrMsg = 0;
  int rc; 

  if (argc != 3) {
    fprintf(stderr, "Usage: %s Database SQL-Statment\n", argv[0]);
    exit(1);
 }

  rc = sqlite3_open(argv[1], &db);

  if (rc) {
    fprintf(stderr, "Can't open database: %s\n", sqlite3_errmsg(db));
    sqlite3_close(db);
    exit(1);
  }

  rc = sqlite3_exec(db, argv[2], callback, 0, &zErrMsg);

  if (rc != SQLITE_OK) {
    fprintf(stderr, "SQL error: %s\n", zErrMsg);
    sqlite3_free(zErrMsg);
  }

  printf("The SQL statement is executed successfully. Please check the database file!\n");
  sqlite3_close(db);

  return 0;
}

編譯方式:
記得加上 " -lsqlite ",若出現如下錯誤,表示沒有安裝 libsqlite3-dev,用apt-get裝一下吧。

[lego@lego-Chen SQLite]$gcc mySqlite.c -lsqlite -o mySqlite
sqlite.c:3:21: fatal error: sqlite3.h: No such file or directory

測試程式:

// 建立資料庫 sqlite.db 與資料表 mytable
[lego@lego-Chen SQLite]$./mySqlite sqlite.db "create table mytable(id int, name char(10));"
The SQL statement is executed successfully. Please check the database file!

// 新增一筆資料
[lego@lego-Chen SQLite]$./mySqlite sqlite.db "insert into mytable values(1, 'lego');"
The SQL statement is executed successfully. Please check the database file!

// 測試資料是否有被新增
[lego@lego-Chen SQLite]$./mySqlite sqlite.db "select * from mytable;"id = 1
name = lego

The SQL statement is executed successfully. Please check the database file!

SQLite 初體驗

一直很想玩這玩意,看了一下,果然是個好玩意!!
雖然我對資料庫還很嫩,不過他有幾個吸引我的地方:
1. 可攜性與跨平台,SQLite Database就是一個檔案,只要你建立的SQLitey資料庫檔案,
Copy 到別的支援 SQLite 的平台上,就可以立即享用。
2. 很方便使用 shell 的管線與資料流重導以及 shell 的字串處理指令,來完成多種應用。

——————————————

當我們安裝完 SQLite 後 (Ubuntu:apt-get install sqlite)
我們就可以使用 sqlite 指令,來使用 SQLite 資料庫。

當你單獨執行 sqlite 指令時,便會進入 SQLite 的 shell 模式,
在該 shell 模式中,有兩種指令可以使用:
1. 直接使用 SQL 語法來建立資料表或新增,刪除,修改,查詢資料表的內容,記得結尾要加上 ";" 。
2. SQLite3 專有的指令,皆以 . 開頭的指令。

以下說明一下,怎麼使用 sqlite 指令來建立資料庫,建立資料表,並新增,刪除,修改,查詢資料表的內容
為了方便說明,亦會穿插一些sqlite內建管理指令(以 . 開頭)。

//  <--  建立資料庫  -->
// 單獨執行 sqlite 指令,便會進入 SQLite 的 shell 模式
// 後面的 SQLite.db 為資料庫檔案的名稱,
// 若該資料庫檔案已存在的話,表示你要使用該資料庫檔案,對該資料庫檔案進行操作。
// 若該資料庫檔案不存在的話,則會在你為該資料庫建立第一張表的時候,
// sqlite 會自動為你建立該資料庫檔案。
// 在此我尚未建立過 SQLite.db 這個資料庫檔案
[lego@lego-Chen SQLite]$sqlite SQLite.db
SQLite version 2.8.17
Enter ".help" for instructions
sqlite>

//  <--  建立第一張資料表  -->
sqlite> create table mytable (id int, name char(10), age int, sex char(5));

// 驗證資料表是否正確被建立
// .tables 是用來看該資料庫中有幾個資料表
sqlite> .tables
mytable

// .schema 是用來看該資料庫中資料表的結構
// 後面可以指定你要看哪一個資料表的結構(ex: .scheam mytable),
// 不加參數的話就是看所有資料表的結構。
sqlite> .schema
create table mytable (id int, name char(10), age int, sex char(5));

//  <--  新增3筆資料  -->
sqlite> insert into mytable values(1, '王小明', 18, '男');
sqlite> insert into mytable values(1, '李小四', 32, '男');
sqlite> insert into mytable values(2, '黃小仙', 28, '女');

//  <--  查詢所有資料表的內容  -->
sqlite> select * from mytable;
1|王小明|18|男
1|李小四|32|男
2|黃小仙|28|女

//  <--  查詢資料表中 id == 1 的內容  -->
sqlite> select * from mytable where id = 1;
1|王小明|18|男
1|李小四|32|男

//  <--  修改資料中的內容,將黃小仙的年紀改為 18  -->
sqlite> update mytable set age = 18 where name = '黃小仙';
// 看看是否修改成功
sqlite> select * from mytable where name = '黃小仙';
2|黃小仙|18|女
sqlite> select age from mytable where name = '黃小仙';
18

//  <--  刪除一筆資料,刪除名字為李小四的資料  -->
sqlite> delete from mytable where name = '李小四';

// 驗證刪除的結果是否正確
sqlite> select * from mytable where name = '李小四';
sqlite> select * from mytable where id = 1;
1|王小明|18|男

經由以上一連串的語法,應該可以掌握基本的使用方式了。

接下來就備忘一下幾個 SQLite 內建指令的功能,在此依然延用上面所建立的資料庫:
(其中.tables與.schema上面已經有示例)

.mode 指令:可調整輸出格式,有下列幾種格式,預設為 list 模式:
"line(s)", "column(s)", "insert", "list", or "html"
測試如下:

//  <--  list mode  -->

sqlite> .mode list
sqlite> select * from mytable;
1|王小明|18|男
2|黃小仙|18|女

//  <--  html mode  -->

// 輸出 html 格式,可搭配 CGI 做應用
sqlite> .mode html
sqlite> select * from mytable;
<TR><TD>1</TD>
<TD>王小明</TD>
<TD>18</TD>
<TD>男</TD>
</TR>
<TR><TD>2</TD>
<TD>黃小仙</TD>
<TD>18</TD>
<TD>女</TD>
</TR>

//  <--  insert mode  -->

// 可以看該資料表的插入語法結構
sqlite> select * from mytable;
INSERT INTO table VALUES(1,'王小明',18,'男');
INSERT INTO table VALUES(2,'黃小仙',18,'女');

//  <--  column mode  -->

// 此模式的輸出,可讀性是最好的,
// 還可以藉由  .header ON|OFF 來選擇開啟表頭欄位。
//           .width 來調整每一個欄位的顯示寬度。
sqlite> .mode column
sqlite> select * from mytable;
1           王小明   18          男
2           黃小仙   18          女

// 啟用 header
sqlite> .header on
sqlite> select * from mytable;
id          name        age         sex
----------  ----------  ----------  ----------
1           王小明   18          男
2           黃小仙   18          女

// 調整欄位寬度

sqlite> .width 2 4 3 3
sqlite> select * from mytable;
id  name  age  sex
--  ----  ---  ---
1   王   18   男
2   黃   18   女

.output:設定輸出結果的流向,預設是 stdout,可以將資料流輸出至檔案

// 將輸出流導入 log 檔案
sqlite> .output log

// 執行完該 SQL 語法後,會發現畫面中沒有任何輸出訊息
sqlite> select * from mytable;

// 退出 sqlite shell
sqlite> .exit

// 會看到本目錄下產生了一個 log file,其內容則為剛剛 SQL 語句輸出內容
[lego@lego-Chen SQLite]$cat log
id  name  age  sex
--  ----  ---  ---
1   王   18   男
2   黃   18   女

// 若要改回螢幕輸出 .output stdout

.dump:可用來備份資料庫 (重要)

// 首先將輸出流設定為你要備份的檔名(ex: SQLite_backup.dump)
sqlite> .output SQLite_backup.dump

// 備份,將指令語法輸出至 SQLite_backup.dump
sqlite> .dump

// 離開 SQLite shell, 並檢查 dump file
sqlite> .exit
[lego@lego-Chen SQLite]$cat SQLite_backup.dump
BEGIN TRANSACTION;
create table mytable (id int, name char(10), age int, sex char(5));
INSERT INTO mytable VALUES(1,'王小明',18,'男');
INSERT INTO mytable VALUES(2,'黃小仙',18,'女');
create table mytable2 (id int, name char(10), age int, sex char(5));
COMMIT;

—————————————

sqlite 指令的其他用法,不進入 SQLite shell,直接進行操作,指令格式如下:
sqlite {參數一:資料庫檔案} {參數二:sqlite內建指令 or SQL語法}

舉例1 :

[lego@lego-Chen SQLite]$sqlite SQLite.db "select * from mytable;"
1|王小明|18|男
2|黃小仙|18|女

舉例2 :

[lego@lego-Chen SQLite]$sqlite SQLite.db ".schema mytable"
create table mytable (id int, name char(10), age int, sex char(5));

舉例3 : 備份資料庫 (之前在shell裡備份的方法,有點蠢)

[lego@lego-Chen SQLite]$sqlite SQLite.db ".dump" > SQLite_backup.dump

由上可知,sqlite指令,可以直接在參數中,下達SQL語法與其內建指令,
因此方便我們用 shell script, awk, sed …等 Linux 字串處理指令,
來進一步處理這些資料,非常方便。

SQLite: Use Perl to access SQLite Database

練習一下怎麼用 Perl 來存取 SQLite,在此紀錄一下實作過程中的版本對決的問題。

使用之前先來建立一個 SQLite 資料庫:

// 進入SQLite的shell模式
[lego@lego-Chen SQLite]$sqlite lego.db
SQLite version 2.8.17
Enter ".help" for instructions
// 建立一個資料表 t
sqlite> create table t (id int, name char(10));
// 插入兩筆資料 1. id = 5, name = lego 2. id = 2, name = kenis
sqlite> insert into t values(5, 'lego');
sqlite> insert into t values(2, 'kenis');
// 檢查資料表內容是否如我們預期
sqlite> select * from t;
5|lego
2|kenis
// 退出 SQLite shell 模式
sqlite> .exit
[lego@lego-Chen SQLite]$

下面的 perl script 可檢查你的 perl 是否有支援 SQLite

#!/usr/bin/perl

use DBI;
my @drvnames=();
@drvnames = DBI->available_drivers();
print "drivers installed:\n";

foreach my $dn (@drvnames) {
	print "$dn\n";
}

然而我在運行上述 Perl Script 時,出現了如下錯誤:(表示沒有安裝libapache-dbi-perl套件)

Can't locate DBI.pm in @INC (@INC contains: /etc/perl /usr/local/lib/perl/5.10.1 /usr/local/share/perl/5.10.1 /usr/lib/perl5 /usr/share/perl5 /usr/lib/perl/5.10 /usr/share/perl/5.10 /usr/local/lib/site_perl .) at /opt/legohome//DBI_Check.pl line 3.
BEGIN failed--compilation aborted at /opt/legohome//DBI_Check.pl line 3.

使用 apt-get install libapache-dbi-perl 後,即可運行上述的 perl script,
結果如下:

drivers installed:
DBM
ExampleP
File
Gofer
Proxy
Sponge

~~~沒有看到 SQLite,表示目前的 perl 不支援,
因此 apt-get install libdbd-sqlite3-perl 裝一下吧,
安裝完後在 Run 一次 perl-sqlite_check.pl:

[lego@lego-Chen ~]$ perl-sqlite.pl
drivers installed:
AnyData
DBM
ExampleP
File
Gofer
Proxy
SQLite
Sponge

用一個 perl script 來 insert 一筆資料到剛剛建立的 SQLite Database (lego.db)

#!/usr/bin/perl

use DBI;
#use strict;

my $dbargs = {AutoCommit => 0,
              PrintError => 1};
my $dbh = DBI->connect("DBI:SQLite:dbname=test.db", "", "", $dbargs);
#插入一筆資料 id = 1, name = ptrancer
$dbh->do("INSERT INTO t VALUES (1, 'ptrancer')");

if ($dbh->err()) {
  die "$DBI:errstr\n";
}
$dbh->commit();
$dbh->disconnect();
print "I have inserted one record\n";
exit;

若出現如下錯誤,表示你安裝的 sqlite 版本跟你 perl 的 sqlite 模組版本不匹配

[lego@lego-Chen SQLite]$./basic_demo.pl
DBD::SQLite::db do failed: file is encrypted or is not a database at ./basic_demo.pl line 10.
:errstr

基本上,在你安裝 perl 的 sqlite 模組時,由套件的檔名就可以知道你的sqlite模組版本,
ex: libdbd-sqlite3-perl,就是 SQLite3。
而你系統上安裝的 SQLite 版本,則可以直接輸入SQLite指令去查看:

[lego@lego-Chen SQLite]$sqlite
SQLite version 2.8.17   (就在這)
Enter ".help" for instructions
sqlite>

由結果可知我 perl 的 SQLite module 是 第三版,系統的 SQLite 是第二版,
因此會出現上述問題,所以就更新一下系統的 SQLite 版本吧(apt-get install sqlite3)。
在執行一次 basic_demo.pl

DBD::SQLite::db do failed: file is encrypted or is not a database at ./basic_demo.pl line 10.
:errstr

很遺憾,發現問題依舊~XD…
原來是因為,我們之前建立lego.db時,系統的SQLite版本還是Ver.2,
因此我們要將 lego.db 由 2版 轉到 3版,指令如下(參考網址):
小記一下:SQLite3的指令是 /usr/bin/sqlite3, 原本的 /usr/bin/sqlite 依然還是 2.x版的,
因此往後建立 Database 時,要記得用 sqlite3 指令。

// 首先用 Ver.2 的 sqlite 指令備份 lego.db
[lego@lego-Chen SQLite]$sqlite lego.db .dump > dump_lego.db
// 把原本的 ver.2 lego.db 砍掉
[lego@lego-Chen SQLite]$rm -rf lego.db
// 用 Ver.3 的 sqlite3 指令,將剛剛備份的資料庫內容,重新建立一個 lego.db
[lego@lego-Chen SQLite]$sqlite3 lego.db < dump_lego.db  

在執行一次 basic_demo.pl

// 先確認目前資料表 t 的內容
[lego@lego-Chen SQLite]$sqlite3 lego.db "select * from t;"
 5|lego
 2|kenis
// 執行 ./basic_demo.pl
[lego@lego-Chen SQLite]$./basic_demo.pl
I have inserted one record
// 再看一次資料表 t 的內容
[lego@lego-Chen SQLite]$sqlite3 lego.db "select * from t;"
 5|lego
 2|kenis
 1|ptrancer   // 新增成功

用一個 perl script 來 select lego.db 的內容

#!/usr/bin/perl

use DBI;

my $dbargs = {AutoCommit => 0,
              PrintError => 1};
// 連線資料庫
my $dbh = DBI->connect("dbi:SQLite:dbname=lego.db", "", "", $dbargs);

if ($dbh->err()) {
  die "$DBI:errstr\n";
}

// 宣告兩個變數 id, name
my $id;
my $name;
my $sql = qq{ select * from t};
my $sth = $dbh->prepare($sql);
$sth->execute();

// 將取得的id欄位資料放到id變數中, name欄位資料放到name變數中
$sth->bind_columns(undef, \$id, \$name);

// 一次取得一筆資料
while ($sth->fetch()) {
  print "id: $id name: $name\n";
}
exit;

執行結果:

[lego@lego-Chen SQLite]$./basic_demo2.pl
id: 5 name: lego
id: 2 name: kenis
id: 1 name: ptrancer

//———————————————————–//
總結一下上述的本版問題:
其實用 perl 存取 SQLite DB 的時候,可以直接在 perl 中建立資料庫,建立資料表,新增,查詢, 修改,
刪除資料表每一筆資料的內容。
此時就不需要動用到系統上的 sqlite 指令,因此也就不會有這個所謂的版本衝突存在。
這次會遇到的版本問題,是因為我用系統的 sqlite 指令,來建立 SQLite DB,
且系統上面安裝的 SQLite 與 perl 使用的 SQLite模組,版本不同所導致,
不過也因為這樣子胡搞瞎搞,學到了一些經驗,但為了一致性,避免麻煩的話,
全都在 Perl 裡做,還是比較好的方法。

關於在 perl 裡,建立資料庫,建立資料表,新增,查詢,修改,刪除資料表每一筆資料的內容可參考下列程式碼:
該程式碼引用至:http://szabgab.com/talks/fundamentals_of_perl/database-using-dbi.html

#!/usr/bin/perl use strict; use warnings; use Getopt::Long qw(GetOptions);
use DBI qw();
my $action; GetOptions("action=s" => \$action);
if (not $action or $action !~ /^(create|insert|selecta|selecth)$/) {
    print <connect("dbi:SQLite:dbname=$dbfile","","", {});
if ($action eq "insert") {
    insert();
}
if ($action eq "selecta") {
    fetch_arrays();
}
if ($action eq "selecth") {
    fetch_hashref();
}

sub create {
    unlink $dbfile if -e $dbfile;
    my $dbh = DBI->connect("dbi:SQLite:dbname=$dbfile","","", {});
    $dbh->do("CREATE TABLE people (id INTEGER PRIMARY KEY, fname VARCHAR(100), lname VARCHAR(100))");
    return;
}

sub insert {
    $dbh->do("INSERT INTO people (id, fname, lname) VALUES(?, ?, ?)", undef, 1, "Gabor", "Szabo");
    $dbh->do("INSERT INTO people (id, fname, lname) VALUES(?, ?, ?)", undef, 2, "Josef", "Kiss");
    return;
}

sub fetch_arrays {
    my $sth = $dbh->prepare("SELECT lname, fname FROM people WHERE id = ?");
    $sth->execute(1);
    while (my @result = $sth->fetchrow_array()) {
        print "lname: $result[0], fname: $result[1]\n";
    }
    $sth->finish;
    return;
}

sub fetch_hashref {
    my $sth = $dbh->prepare("SELECT lname, fname FROM people WHERE id = ?");
    $sth->execute(1);
    while (my $result = $sth->fetchrow_hashref("NAME_lc")) {
        print "lname: $result->{lname}, fname: $result->{fname}\n";
    }
    $sth->finish;
    return;
}

不錯的範例參考網站:

http://szabgab.com/talks/fundamentals_of_perl/database-using-dbi.html

http://quickies.andreaolivato.net/post/123847804/perl-and-sqlite3-quick-start-example-guide

http://search.cpan.org/~adamk/DBD-SQLite-1.35/lib/DBD/SQLite.pm

Android – Activity (1)

今天看了一下Android 中 Activity 切換的觀念,感覺實作的細節有點多 (有點懶得紀錄程式碼),

首先我們要知道 Activity 的切換,必須透過 Intent 這個類別來達成,其三者關係如下圖:

『網路上許多人將 Intent 理解成 "意圖",也就是說它可以被試圖去喚醒某個 Activity 或者 Service 』。

不過除了可以透過 Intent 來喚醒某個 Activity 或 Service 之外,還可以透過 Intent 讓 Activity 來互相傳遞資料。

————————————————-

思考:Activity 之間是否共享資料:

當一個 A Activity 跳到另一個 B Activity,A 與 B 這兩個 Activity 彼此之間,

可能不需要或者可能需要共享資料,這時可以有三種情況:

1. B 不需要 A 的資訊,A 也不需要 B 的資訊

2. B 需要 A 的資訊

3. B 需要 A 的資訊,A 也需要 B 的資訊

第 1 種情況: B 不需要 A 的資訊,A 也不需要 B 的資訊

Intent 不用裝載任何資料,因此直接告訴 Intent 要由 A 喚醒 B 後,再丟到 startActivity(intent) 中即可。

告訴 Intent 由 A Activity 喚醒 B Activity 的方式有兩種:

> 在建構子中描述:

    Intent intent = new Intent(A.this, B.class);

> 使用 Intent 的 setClass():

   Intent intent = new intent();

   intent.setClass(A.this, B.class);

第 2 種情況:B 需要 A 的資訊

這時要思考的是,我們要丟的資料是甚麼 ? 是基本資料型別 ( int , string, boolean … 等 ),還是自定義類別 ?

雖然 Intent 提供了一系列存資料(put)與取資料(get)的function,且用法跟Bundle類似,

不過我覺得只要記得可以用 Bundle 類別來綑綁資料,再用 intent.putExtras(bundle) 把 Bundle 存到 intent 中傳遞就好了,

如果是基本資料型別的話,可使用 Bundle 類別提供的一系列 putXXXXExtra(Key, Value)方法

(XXXX為資料型別,如:int, string, boolean),來存放資料。

如果是自定義類別的話,可以 Bundle 類別提供的 putSerializable(Key,Object)  putParcelable(Key, Object) 來實現。

關於 putSerializable 與 putParcelable 是比較進階的用法,可參考 http://blog.csdn.net/xyz_lmn/article/details/5908355 。

而在目標 Activity 取出資料的方法,以此例來說,就是在 B 的 Activity 中使用

Intent intent = B.this.getIntent();  // 取得 A Activity 傳過來的 intent

Bundle bundle = intent.getExtras();  // 再從該 intent 取得資料

第 3 種情況:B 需要 A 的資訊,A 也需要 B 的資訊

這種情況表示,A 啟動 B 後,當 B 結束時,需要回傳資料給 A,

因此在這種情況下,不可以用 startActivity(Intent) 來切換 Activity ,必須使用 startActivityForResult(intent, requestCode) 。

當 A 採用 startActivityForResult() 喚醒 B 時,B 結束完,再度回到 A 時,

便會觸發 onActivityResult(int requestCode, int resultCode, Intent data) 事件,

( 使用 startActivity() 的話,返回時不會觸發 onActivityResult() 事件)

該事件中,有三個參數:

1. requestCode :

這個值,等於 startActivityForResult() 中的 requestCode,由呼叫者 Activity 設定。

2. resultCode :

這個值是由 被呼叫者 Activity 設定,用來得知被呼叫者 Activity 結束時的訊息。

例如:假設 B Activity 中有一個詢問使用者意願的問題,讓使用者可以點選 Button 選擇 『是』 或 『否』,

當使用者選擇完畢後,便結束 B 回到 A,此時 B Activity 就可以使用 setResult(int resultCode) ,來設定使用者選擇的意願。

A 便可從 resultCode 取得使用者的意願。

3. data :

在上述中 B Activity 可以用 setResult(int resultCode) 來設定 resultCode 給 A Activity 知道,

然而還有一個 setResult() 的多載方法 setResult(int resultCode, Intent data),不僅可回傳 resultCode ,

亦可回傳資料給 A Activity,而 A Activity 便可從 onActivityResult() 中的 data 取得該資料。

————————————-

由於寫範例程式碼,有點累,暫時先備忘一下觀念與常用的函式有哪些。

XScale 255 – 用 JTAG 刷 Bootloader

我的開發平台是 ubuntu 11.04,用 JTAG 燒 Bootloader 時出現如下圖錯誤。

./Jflash-XSBASE: error while loading shared libraries: libstdc++-libc6.2-2.so.3:
 cannot open shared object file: No such file or directory

解決方法是到:  http://simnovo.net/ubuntu-11-04-h3c/ 下載:libstdc++-libc6.2-2.so.3

並將該動態函式庫複製到 /usr/lib 中。即可正常執行 Jflash-XSBase  x-boot255 ( 記得該指令需要root權限)

如果開發平台是 CentOS 6.2 (i386) 的話,就安裝 compat-libstdc++-296 套件即可。

HOG Detector ~ Training

最近糊里糊塗的完成了 HOG Detector 對於輪胎偵測的訓練(效果挺優 !!),

都要感謝此網站的達人:http://blog.baifaces.com/baifaces/blog/work/opencv-hog-peopledetector-trainning.html

在此也記錄一下,我實作時的疑難雜症吧!!

實作平台是:Ubuntu 11.04

釋出訓練程式的網站:http://pascal.inrialpes.fr/soft/olt/   (載點)   (樣本檔)

樣本檔容量很大,如果只是要單純訓練自己的樣本的話,大可不用抓。

步驟紀錄如下:

[lego@lego HOG]$ wget http://pascal.inrialpes.fr/soft/olt/OLTbinaries.zip
[lego@lego HOG]$ unzip OLTbinaries.zip
[lego@lego HOG]$ cd OLTbinaries
// 建立樣本資料夾 train 與 test
[lego@lego OLTbinaries]$  mkdir train test
// 把你的正樣本 copy 到 train 與 test 目錄中 (pos為你放正樣本圖片的資料夾)
[lego@lego OLTbinaries]$  cp -r pos train/
[lego@lego OLTbinaries]$  cp -r pos test/
// 把你的負樣本 copy 到 train 與 test 目錄中 (neg為你放負樣本圖片的資料夾)
[lego@lego OLTbinaries]$  cp -r neg train/
[lego@lego OLTbinaries]$  cp -r neg test/
// 建立正負樣本的 list
[lego@lego OLTbinaries]$  find train/pos > train/pos.lst
[lego@lego OLTbinaries]$  find test/pos > test/pos.lst
[lego@lego OLTbinaries]$  find train/pos > train/neg.lst
[lego@lego OLTbinaries]$  find test/pos > test/neg.lst
// 做完上述指令後,需要把上面的每一個 lst 檔的第一行 刪掉
// 修改 runall.sh 把其中的 WIDTH 與 HEIGHT 改成你自己的正樣本大小
[lego@lego OLTbinaries]$  vi runall.sh
WIDTH=64; export WIDTH
HEIGHT=128; export HEIGHT
// 接下來就開始訓練了 !!! (我訓練的正樣本有 1133 張 size 50 x 50,負樣本有 680 張 size 512x384)
// 跟 AdaBoost 比起來,HOG Detector 的訓練時間算是很快!!
[lego@lego OLTbinaries]$  sh runall.sh
// 等待 runall.sh 跑完後,便可看到 OLTbinaries/HOG 目錄下,產生了一個 model file
// 此即為我們訓練好的檔案。
[lego@lego OLTbinaries]$ ls HOG/model_4BiSVMLight.alt
HOG/model_4BiSVMLight.alt

以上為訓練過程,以下備註一下注意事項:

1. 訓練的正、負圖片可以是 png 或 jpg ( 我用 bmp 時,會出錯 ),還有就是副檔名要小寫。
2. 若要改變訓練完的 model 檔名稱與位置,可在 runall.sh 中修改。

做完訓練,接下來就是測試訓練檔好不好用啦!!

在此它有提供 runonimage.sh 讓我們測試,用法如下:
## 使用前記得先修改 runonimage.sh 內的正樣本圖片長、寬(與你訓練的正樣本長、寬要一樣),不然會出錯。
WIDTH=64; export WIDTH
HEIGHT=128; export HEIGHT

# runonimage.sh {image name/image directory/list file} {out text file} {out image file/out image dir}

// 總共有三個參數

參數1: 可直接指定預測試的圖片檔名 (ex: photo.jpg ),或者是一個存放圖片清單的檔案
       ( ex: 存放所有你要測試圖片清單的檔案 image.lst 內容如:test/photo1.jpg )
參數2: 測試過程中,可能會有一些測試結果的資訊, 這些資訊會存入該參數指定的檔案
參數3: 測試完畢後,該程式會將偵測物件的結果資訊,畫在圖片上,因此該參數便是指定這張被繪製偵測結果資訊的圖片的位置,
       如果你指定的是圖片檔名,則會以該檔名進行儲存;
       如果你指定的是一個目錄,則會以原始測試的檔名,儲存在你指定的目錄中。

// 實際指令下法

# 1 測試單張圖片
[lego@lego OLTbinaries]$ sh runonimage.sh photo.jpg result.txt result.jpg

# 2 測試大量圖片
[lego@lego OLTbinaries]$ sh runonimage.sh image.lst result.txt result_folder/

測試的圖片,還是盡量使用 jpg 或 png 且輸出的附檔名一定要是小寫,不然有時候跑起來程式會一直出錯,挺麻煩的。

在此SHOW三張偵測結果圖吧 (效果不賴):

當然 ~ 有時候難免有誤判 。

 

以後有空再把它整進 Android 手機中試看看好了 !!! 先降 ~

 

 

 

Linux : ncurses.h – 指定 Console 中的座標打印字元

參考網站:http://www.faqs.org/docs/Linux-HOWTO/NCURSES-Programming-HOWTO.html

由於上一篇已經介紹過在Dev C/C++中使用類似功能,在此記錄一下用法。

以下的程式是將使用者的輸入固定輸出到console中某個特定的位置:

#include <stdio.h>
#include <string.h>
#include <ncurses.h>

int main() {
    int row, col;
    initscr();
    // 關閉 getch() 的顯示
    noecho();
    char str[] = "Number is : ";
    mvprintw(0, 0, "Number is : ");
    int i;

    while(1) {
      getmaxyx(stdscr, row, col);
      char intputChar;
      intputChar = getch();
      //      rows, cols
      mvprintw(0, 13,"%c", intputChar);
    }
}

編譯時記得加 -lcurses

gcc curses.c -lcurses

沒有加會出現如下訊息:

[lego@lego stdout]$ gcc ncurses.c
/tmp/ccs3bbYP.o: In function `main':
ncurses.c:(.text+0xa): undefined reference to `initscr'
ncurses.c:(.text+0xf): undefined reference to `noecho'
ncurses.c:(.text+0x48): undefined reference to `mvprintw'
ncurses.c:(.text+0x4d): undefined reference to `stdscr'
ncurses.c:(.text+0x56): undefined reference to `stdscr'
ncurses.c:(.text+0x6e): undefined reference to `stdscr'
ncurses.c:(.text+0x77): undefined reference to `stdscr'
ncurses.c:(.text+0x8f): undefined reference to `stdscr'
ncurses.c:(.text+0x97): undefined reference to `wgetch'
ncurses.c:(.text+0xc0): undefined reference to `mvprintw'
collect2: ld returned 1 exit status

Dev C/C++ : conio2.h – 指定 Console 中的座標打印字元

我們在寫Console程式時,如果要打印如下字串的話,如下圖,


往往是用 printf(),很蠢的由打印結果,來增減 printf() 內的『空格』數量,程式碼如下:

#include <stdio.h>

int main(void)
{
  printf("\n\n");
  printf("  Hello World\n\n");
  getchar();
  return 0;
}

當文字介面的畫面設計的非常複雜時,你若採用上述的方式來達成,就要開始土法煉鋼的慢慢try出你要的排版,非常不方便。
因此在 Dev C/C++ 中,我們可以透過 include conio2.h 檔,來設定 printf() 打印的起始座標位址,程式碼如下,
即可達到上述程式碼的效果,非常方便:

#include <stdio.h>
#include <conio2.h>

int main(void)
{
  gotoxy(2,3);
  printf("Hello World\n\n");
  getchar();
  return 0;
}

不過要在 Dev C/C++ 中使用 conio2.h 時,需要進行一些設定,如下:

1. 到 http://old.devpaks.org/show.php?devpak=75 頁面下載 conio-2.0-1mol.DevPak

2. 下載完後,直接開啟 conio-2.0-1mol.DevPak , 操作畫面如下:

點 『Install』後再點,『Finish』,便會看到如下畫面,便完成安裝:

設定 Dev C/C++,點擊 『Tools』 -> 『Compiler Options』,並設定畫面如下(加上 -lconio):

大功告成 !!! (參考網站:http://allen501pc.blogspot.com/2008/04/dev-c-include-coniohconio2h.html )

在 Linux 上亦可以使用 ncurses.h 標頭檔達到此功能,可參見:http://lego.twgg.org/?p=843

## 備註一下

gotoxy(int, int) 的起點是 (1, 1) 不是 (0, 0)

如果想要參考範例的話,可至Dev安裝目錄下的 Example\conio 目錄中參考。

conio的參考手冊:http://conio.sourceforge.net/docs/conio.pdf

Lego Google +
Couldn't get data from google+
瀏覽記錄
您的IP是: 38.107.179.231

目前線上訪客數: 0 人次
本日訪客數: 7 人次
本月訪客數:5580 人次
本頁面人氣:775 人次
Google Adsense