小男孩‘自慰网亚洲一区二区,亚洲一级在线播放毛片,亚洲中文字幕av每天更新,黄aⅴ永久免费无码,91成人午夜在线精品,色网站免费在线观看,亚洲欧洲wwwww在线观看

分享

MySQL數(shù)據(jù)庫(kù)技術(shù)(18)

 Ralf_Jones 2006-07-19
破釜沉舟 http://www.


文章類(lèi)別:MySQL  發(fā)表日期:2005-09-27      閱讀次數(shù): 129

 

3.8 解決方案隨筆
    本節(jié)內(nèi)容相當(dāng)雜;介紹了怎樣編寫(xiě)解決各種問(wèn)題的查詢(xún)。多數(shù)內(nèi)容是在郵件清單上看到的解決問(wèn)題的方案(謝謝清單上的那些朋友,他們?yōu)榻鉀Q方案作了很多工作)。
    3.8.1 將子選擇編寫(xiě)為連接
    MySQL自3.24版本以來(lái)才具有子選擇功能。這項(xiàng)功能的缺少是MySQL 中一件常常令人惋惜的事,但有一件事很多人似乎沒(méi)有認(rèn)識(shí)到,那就是用子選擇編寫(xiě)的查詢(xún)通常可以用連接來(lái)編寫(xiě)。實(shí)事上,即使MySQL 具有了子查詢(xún),檢查用子選擇編寫(xiě)的查詢(xún)也是一件苦差事;用連接而不是用子選擇來(lái)編寫(xiě)會(huì)更為有效。
    1. 重新編寫(xiě)選擇匹配值的子選擇
    下面是一個(gè)包含一個(gè)子選擇查詢(xún)的樣例,它從score 表中選擇所有測(cè)試的學(xué)分(即,忽略測(cè)驗(yàn)的學(xué)分):

    可通過(guò)將其轉(zhuǎn)換為一個(gè)簡(jiǎn)單的連接,不用子選擇也可以編寫(xiě)出相同的查詢(xún),如下所示:

    下面的例子為選擇女學(xué)生的學(xué)分:

    可將其轉(zhuǎn)換為連接,如下所示:

    這里是一個(gè)模式,子選擇查詢(xún)?nèi)缦滦问剑?br>
    這樣的查詢(xún)可轉(zhuǎn)換為如下形式的連接:

    2. 重新編寫(xiě)選擇非匹配值的子選擇查詢(xún)
    另一種常用的子選擇查詢(xún)是查找一個(gè)表中有的而另一個(gè)表中沒(méi)有的值。正如以前所看到的那樣,“那些未給出的值”這一類(lèi)的問(wèn)題是LEFT JOIN 可能有用的一個(gè)線(xiàn)索。下面的查詢(xún)包含一個(gè)子選擇(它尋找那些全勤的學(xué)生):


    3.8.2 檢查表中未給出的值
    我們已經(jīng)在3 . 6節(jié)“檢索記錄”中看到,在要想知道一個(gè)表中哪些值不出現(xiàn)在另一表中時(shí),可對(duì)兩個(gè)表使用LEFT JOIN 并查找那些從第二個(gè)表中選中NULL 的行。并用下列兩個(gè)表舉例:

    現(xiàn)在讓我們來(lái)考慮一種更為困難的情況,“缺了哪些值”。對(duì)于第1 章中提到的學(xué)分保存方案中,有一個(gè)列出學(xué)生的student 表,一個(gè)列出已經(jīng)出現(xiàn)過(guò)的學(xué)分事件的event 表,以及列出每個(gè)學(xué)生的每次學(xué)分事件學(xué)分的一個(gè)score 表。但是,如果一個(gè)學(xué)生在某個(gè)測(cè)試或測(cè)驗(yàn)的同一天病了,那么score 表中將不會(huì)有這個(gè)學(xué)生的該事件的學(xué)分,因此,要進(jìn)行測(cè)驗(yàn)或測(cè)試的補(bǔ)考。我們?cè)鯓硬檎疫@些缺少了的記錄,以便能保證讓這些學(xué)生進(jìn)行補(bǔ)考?問(wèn)題是要對(duì)所有的學(xué)分事件確定哪些學(xué)生沒(méi)有某個(gè)學(xué)分事件的學(xué)分。換個(gè)說(shuō)法,就是我們希望知道學(xué)生和事件的哪些組合不出現(xiàn)在學(xué)分表中。這就是我們希望LEFT JOIN 所做的事。這個(gè)連接不像前例中那樣簡(jiǎn)單,因?yàn)槲覀儾粌H僅要查找不出現(xiàn)在單列中的值;還需要查找兩列的組合。
    我們想要的這種組合是所有學(xué)生/事件的組合,它們由student 表與event 表的叉積產(chǎn)生:
    FROM student, event
   然后我們?nèi)〕龃诉B接的結(jié)果,與score 表執(zhí)行一個(gè)LEFT JOIN 語(yǔ)句找出匹配者:
    FROM student, event
    LEFT JOIN score ON student.student_id = score.student.id
                    AND event.event_id = score.event_id
    請(qǐng)注意,ON 子句使得score 表中的行根據(jù)不同表中的匹配者進(jìn)行連接。這是解決本問(wèn)題的關(guān)鍵。LEFT JOIN 強(qiáng)制為由student 和event 表的叉連接生成的每行產(chǎn)生一個(gè)行,即使沒(méi)有相應(yīng)的score 表記錄也是這樣。這些缺少的學(xué)分記錄的結(jié)果行可通過(guò)一個(gè)事實(shí)來(lái)識(shí)別,就是來(lái)自score 表的列將全是NULL 的。我們可在WHERE 子句中選出這些記錄。來(lái)自score 表的任何列都是這樣,但因?yàn)槲覀儾檎业氖侨鄙俚膶W(xué)分,測(cè)試score 列從概念上可能最為清晰:
    WHERE score.score IS NULL
    可利用ORDER BY 子句對(duì)結(jié)果進(jìn)行排序。兩種最合理的排序分別是按學(xué)生和按事件進(jìn)行,我們選擇第一種:
    ORDER BY student.student_id, event.event_id
    現(xiàn)在需要做的就是命名我們希望在輸出結(jié)果中看到的列。最終的查詢(xún)?nèi)缦拢?br>    SELECT
        student.name, student.student_id,
        event.date, event,event_id, event.type
    FROM
        student,event
        LEFT JOIN score ON student.student_id = score.student_id
                        AND event.event_id = score.event_id
    WHERE
        score.score IS NULL
    ORDER BY
        student.student_id, event.event_id
    運(yùn)行此查詢(xún)得出如下結(jié)果:

    這里有一個(gè)問(wèn)題要引起注意。此輸出列出了學(xué)生的ID 和事件的I D。student_id 列出現(xiàn)在student 和score 表中,因此,開(kāi)始您可能會(huì)認(rèn)為選擇列表可以給出student.student_id 或score . student _ id。但實(shí)際不是這樣,因?yàn)槟軌蛘业礁信d趣記錄的基礎(chǔ)是所有學(xué)分表字段返回N U L L。選擇score.student_id 將只在輸出中產(chǎn)生NULL 值的列。類(lèi)似的推理可應(yīng)用到event_id 列,它也出現(xiàn)在event 和score 表中。
    3.8.3 執(zhí)行UNION 操作
    如果想通過(guò)從具有相同結(jié)構(gòu)的多個(gè)表中建立一個(gè)結(jié)果集,可在某些數(shù)據(jù)庫(kù)系統(tǒng)中使用某種UNION 語(yǔ)句來(lái)實(shí)現(xiàn)。MySQL 沒(méi)有UNION(至少直到3 . 2 4版還沒(méi)有),但有許多辦法來(lái)解決這個(gè)問(wèn)題,下面是兩種可行的方案:
    ■ 執(zhí)行多個(gè)SELECT 查詢(xún),每個(gè)表執(zhí)行一個(gè)。如果不關(guān)心所選出行的次序,這樣做就行了。
    ■ 將每個(gè)表中的行選入一個(gè)臨時(shí)存儲(chǔ)表,然后選擇該表的內(nèi)容。這樣可對(duì)行按所需的次序進(jìn)行排序。在MySQL 3.23版及以后的版本中,可通過(guò)允許服務(wù)器創(chuàng)建存儲(chǔ)表來(lái)解決這個(gè)問(wèn)題。而且,還可以使該表為臨時(shí)表,以便在您與服務(wù)器的會(huì)話(huà)結(jié)束時(shí),自動(dòng)刪除該表。
    在下面的代碼中,我們明確地刪除該表使服務(wù)器釋放與其有關(guān)的資源。如果客戶(hù)機(jī)會(huì)話(huà)將繼續(xù)執(zhí)行進(jìn)一步的查詢(xún),這樣做很有好處。為了取到更好的性能,還可以利用HEAP(在內(nèi)存中)表。

    對(duì)于3 . 2 3版本,除了必須自己明確定義hold_tbl 表中的列外,其想法是類(lèi)似的,而且結(jié)尾處的DROP TABLE 是強(qiáng)制性的,用來(lái)防止在以下客戶(hù)機(jī)會(huì)話(huà)生命周期之后繼續(xù)存在:

    3.8.4 增加序列號(hào)列
    如果用A LTER TABLE 增加AUTO_INCREMENT 列,則該列用序列號(hào)自動(dòng)地填充。下面這組mysql 會(huì)話(huà)中的語(yǔ)句示出了怎樣創(chuàng)建一個(gè)表,在其中存放數(shù)據(jù),然后增加一個(gè)AUTO_INCREMENT 列:

    3.8.5 對(duì)某個(gè)已有的列進(jìn)行排序
    如果有一個(gè)數(shù)值列,可對(duì)其按如下進(jìn)行排序(或?qū)ζ渲嘏判?,如果已?duì)其排過(guò)序,但刪除了行并且想要對(duì)值重新排序使其連續(xù)):
    ALTER TABLE t MODIFY i INT NULL
    UPDATE t SET i = NULL
    ALTER TABLE t MODIFY i INT UNSIGNED AUTO_INCREMENT NOT NULL PRIMARY KEY
    但是有一種更容易的方法,那就是刪除該列,然后再作為一個(gè)A U TO_INCREMENT 列追加它。A LTER TABLE 允許指定多個(gè)活動(dòng),因此,上述工作可在單個(gè)語(yǔ)句中完成:
    ALTER TABLE t
    DROP i,
    ADD i INT UNSIGNED AUTO_INCREMENT NOT NULL PRIMARY KEY
    3.8.6 非正常次序的串
    假如有一個(gè)表示體育機(jī)構(gòu)人員的表,如橄欖球隊(duì),如果按人員職位進(jìn)行排序,以便以特殊的順序表示它,如:教練、教練助理、四分衛(wèi)、流動(dòng)后衛(wèi)、接球員、巡邏員等??蓪⒘卸x為ENUM 并按希望出現(xiàn)的順序定義枚舉元素。對(duì)該列的排序?qū)?huì)以所指定的順序自動(dòng)進(jìn)行。
    3.8.7 建立計(jì)數(shù)表
    在第2章的“使用序列”小節(jié)中,我們介紹了怎樣利用L A S T _ I N S E RT_ID(expr) 生成一個(gè)序列。那個(gè)例子說(shuō)明了怎樣利用單列的表進(jìn)行計(jì)數(shù)。那樣做對(duì)于只需要單個(gè)計(jì)數(shù)器的情形能夠滿(mǎn)足需要,但是,如果需要幾個(gè)計(jì)數(shù)器,該方法將會(huì)引起不必要的表重復(fù)。假如有一個(gè)Web 站點(diǎn)并且想要在幾個(gè)頁(yè)面上放置“此頁(yè)面已經(jīng)被訪(fǎng)問(wèn)nnn 次”這樣的計(jì)數(shù)器。那么為每個(gè)具有一個(gè)計(jì)數(shù)器的頁(yè)面建立一個(gè)單獨(dú)的表就有些多余了。避免創(chuàng)建多個(gè)計(jì)數(shù)器表的一種方法是建立一個(gè)兩列的表。其中一列存放計(jì)數(shù)值;另一列存放計(jì)數(shù)器名。這時(shí)仍然可以使用LAST _ INSERT_ID( ) 函數(shù),但可用計(jì)數(shù)器名來(lái)決定用哪一行。這個(gè)表如下所示:
    CREATE TABLE counter
    (
    count INT UNSIGNED,
    name varchar(255) NOT NULL PRIMARY KEY
    )
    其中計(jì)數(shù)器名為一個(gè)串,從而可以調(diào)用任何想要的計(jì)數(shù)器,我們將其定義為PRIMARY KEY 以免名稱(chēng)重復(fù)。這里假定使用這個(gè)表的應(yīng)用程序知道他們將使用的名稱(chēng)。對(duì)于前面所說(shuō)的Web 計(jì)數(shù)器,可通過(guò)利用文件樹(shù)中每個(gè)頁(yè)面的路徑名作為其計(jì)數(shù)器名的方法,保證計(jì)數(shù)器名的唯一性。例如,要為站點(diǎn)的主頁(yè)建立一個(gè)新計(jì)數(shù)器,可執(zhí)行下列語(yǔ)句:
    INSERT INTO counter(name) VALUES("index.html")
    它用零值初始化稱(chēng)為“ index.html”的計(jì)數(shù)器。為了生成序列中的下一個(gè)值,增加表中相應(yīng)行的計(jì)數(shù)值,然后用LAST _ INSERT_ID( ) 檢索它:
    UPDATE counter
    SET count = LAST_INSERT_ID(count+1)
    WHERE name = "index.html"
    SELECT LAST_INSERT_ID()
    另一種方法是不用LAST _ INSERT_ID( ) 增加計(jì)數(shù)器的值,如下所示:
    UPDATE counter SET count = count+1 WHERE name = "index.html"
    SELECT count FROM counter WHERE name = "index.html"
    然而,如果另一個(gè)客戶(hù)在您發(fā)布U P D ATE 語(yǔ)句與SELECT 語(yǔ)句之間增加了該計(jì)數(shù)器的值,則這種方法工作不正常。不過(guò)可在此兩條語(yǔ)句的前后分別放置LOCK TABLES 和U N L O C KTABLES,在您使用該計(jì)數(shù)器時(shí)阻塞其他客戶(hù),以解決上述問(wèn)題。但用L A S T _ I N S E RT_ID( )方法完成同樣的工作更為容易一些。因?yàn)樗闹凳强蛻?hù)專(zhuān)用的,您總能得到自己插入的值,而不是其他客戶(hù)插入的值,而且不必阻塞其他客戶(hù)使代碼復(fù)雜化。
    3.8.8 檢查表是否存在
    在應(yīng)用程序內(nèi)部知道一個(gè)表是否存在有時(shí)很有用。為了做到這一點(diǎn),可使用下列任一條語(yǔ)句:
    SELECT COUNT(*) FROM tb1_name
    SELECT * FROM tb1_name WHERE 1=0
    如果指定的表存在,則上述兩條語(yǔ)句都將執(zhí)行成功,如果不存在,則都失敗。它們是這種測(cè)試的很好的查詢(xún)。它們執(zhí)行速度快,所以不會(huì)費(fèi)太多的時(shí)間。這種方法最適合您自己編寫(xiě)的應(yīng)用程序,因?yàn)槟梢詼y(cè)試查詢(xún)的成功與失敗并采取相應(yīng)的措施。但在從mysql 運(yùn)行的批量腳本中不特別有用,因?yàn)榘l(fā)生錯(cuò)誤時(shí)除了終止運(yùn)行外不可能做任何事(或者可以忽略相應(yīng)的錯(cuò)誤,但是顯然無(wú)法再運(yùn)行該查詢(xún)了)。


破釜沉舟 http://www.

    本站是提供個(gè)人知識(shí)管理的網(wǎng)絡(luò)存儲(chǔ)空間,所有內(nèi)容均由用戶(hù)發(fā)布,不代表本站觀(guān)點(diǎn)。請(qǐng)注意甄別內(nèi)容中的聯(lián)系方式、誘導(dǎo)購(gòu)買(mǎi)等信息,謹(jǐn)防詐騙。如發(fā)現(xiàn)有害或侵權(quán)內(nèi)容,請(qǐng)點(diǎn)擊一鍵舉報(bào)。
    轉(zhuǎn)藏 分享 獻(xiàn)花(0

    0條評(píng)論

    發(fā)表

    請(qǐng)遵守用戶(hù) 評(píng)論公約

    類(lèi)似文章 更多