極簡邏輯表達式的設計和查詢

来源:http://www.cnblogs.com/ljhdo/archive/2017/07/08/5477882.html
-Advertisement-
Play Games

在資料庫開發中,對兩個關係表進行連接查詢,能夠直接做“邏輯或”的查詢,而對於邏輯與和邏輯非的查詢,則稍複雜點,需要編寫額外的代碼來實現。在關係型資料庫中,所謂的連接,實際上是集合的包含,只要包含一項,就滿足連接條件,實現的邏輯或,這種設計,能夠滿足絕大多數的查詢需求。有時,對於一條數據,可能需要通過 ...


在資料庫開發中,對兩個關係表進行連接查詢,能夠直接做“邏輯或”的查詢,而對於邏輯與和邏輯非的查詢,則稍複雜點,需要編寫額外的代碼來實現。在關係型資料庫中,所謂的連接,實際上是集合的包含,只要包含一項,就滿足連接條件,實現的邏輯或,這種設計,能夠滿足絕大多數的查詢需求。有時,對於一條數據,可能需要通過多個邏輯表達式來定性,比如,判定一篇文章是否跟Microsoft Azure有關,通常簡單的做法是從多個關鍵字的邏輯組合來定性:文章中同時含有關鍵字“Microsoft”和“Azure”,或者同時含有關鍵字“Windows”和“Azure”,把這種邏輯組合抽象成表達式,就是:( Microsoft & Azure ) | ( Windows & Azure )。

邏輯表達式的基本操作符是:與(&),或(|)和非(!),邏輯表達式的最小組合是:A&B,A|B 和 !A。關係型資料庫的開發人員,在設計邏輯表達式時,必須保證滿足業務需求,同時,儘可能支持多種邏輯組合,通常情況下,按照表達式的關係,我們把邏輯表達式拆分成四個元數據類型:Expression(表達式),SubExpression(子表達式),Operator(操作符)和Operand(操作數)。

在我目前接觸的項目中,業務需求的邏輯表達式的組合相對簡單,標準的表達式格式如下所示:

Expression = ((A & B) | (C & D)) & !(E | F)

該邏輯表達式表示:查詢語句返回的結果集中,不能包含E和F,但是,必須包含A和B,或者包含C且不包含D。

對於該類表達式,我們可以抽象成更為通用的邏輯公式是:

Expression= (SubExpression1 | SubExpression2 | ...) & !ExcludeExpression
SubExpression=Operand1 & Operand2 & ...
ExcludeExpression=Operand1 | Operand1 | ...

這種高度概括的表達式蘊含的邏輯關係是:SubExpression中操作數之間的關係是邏輯與,ExcludeExpression中操作數之間的關係是邏輯或,該表達式蘊含的意思是:任意一條數據,不能夠包括ExcludeExpression中的任何一個項目,但是,必須至少滿足一個子表達式(SubExpression),子表達式中的項目都是邏輯與的關係。

一,用關係表存儲邏輯表達式

舉個例子,為了描述方便,我們用下麵的邏輯表達式來說明:

Expression= ((A & B & C) | (C & D)) & !(E | F)

該表達式可以拆分成三個子表達式:A & B & C,C & D, !(E | F),對於子表達式 (A & B & C,C & D),其操作數之間的關係都是邏輯與;而對於子表達式!(E | F),其操作數之間的關係是邏輯或。

1,通過XML文檔表示邏輯表達式

邏輯表達式的子表達式之間,不是無關係的,為了在不同的應用程式間傳遞邏輯表達式,數據結構必須包含該表達式的所有關係和操作數,XML文檔特別適合表達有特定關係的數據結構,按照之前拆分的四種元數據類型,我們把XML文檔定義為三種不同的標簽,分別是<Expression>,<SubExpression>和<Operand>,併為標簽設置不同的屬性,通過格式化的XML實現邏輯表達式的轉存,示例如下:

declare @xml xml
set @xml='
<Expression ID="1">
  <SubExpression ID="1" OperandType="Tag" Operator="and">
    <Operand ID="1" />
    <Operand ID="2" />
    <Operand ID="3" />
  </SubExpression>
  <SubExpression ID="2" OperandType="Tag" Operator="and">
    <Operand ID="4" />
    <Operand ID="5" />
  </SubExpression>
  <SubExpression ID="3" OperandType="Tag" Operator="not">
    <Operand ID="6"  />
    <Operand ID="7"  />
  </SubExpression>
</Expression>'

2,把邏輯表達式存儲到關係表

XML格式的文檔適合數據的傳遞,不適合直接存儲在關係型資料庫中,並且XML格式的文檔也不利於對數據執行查詢操作,因此,必須把XML文檔存儲到數據表中,以利用關係資料庫引擎執行集合操作的優勢,實現數據的快速查詢。當應用程式接收到這個XML文檔,必須把XML蘊含的表達式拆分成:表達式(Expression),子表達式(SubExpression),操作數(Operand)和操作符(Operator),例如,我們可以利用以下腳本創建數據表:

create table dbo.Operands
(
    ExpressionID bigint not null,    
    SubExpressionID smallint not null,
    OperandID bigint not null,          --EntityID, TagID
    Operator varchar(8) not null,       --&,!
    OperandType varchar(8) not null,    --Entity,Tag
    primary key clustered(ExpressionID,SubExpressionID,OperandID)
)
with(data_compression=page);
go

註:OperandType是操作數的類型,分為:Entity和Tag,相應的操作數ID(OperandID)就是EntityID和TagID,本例只使用TagID,EntityID沒有使用。

SQL Server內置函數用於解析XML文檔,通過XML解析函數,我們可以利用TSQL腳本把XML文檔插入到表Operands中。這意味著,在表Operands中,當Operator列值為not時,表示邏輯非,表示OperandID是<ExcludeExpression>標簽存儲的操作數;當Operator列值為and時,表示邏輯與,表示OperandID是<SubExpression>標簽存儲的操作數。

;with cte_Expressions as 
(
    select e.v.query('.') as Expression
        ,e.v.value('@ID','int') as ExpressionID
    from @xml.nodes('/Expression') as e(v)
)
,cte_SubExpression as 
(
    select e.ExpressionID
        ,se.SubExpression
        ,se.SubExpressionID
        ,se.OperandType
        ,se.Operator
    from cte_Expressions e
    cross apply
    (
        select t.v.query('.') as SubExpression
            ,t.v.value('@ID','int') as SubExpressionID
            ,t.v.value('@OperandType','varchar(16)') as OperandType
            ,t.v.value('@Operator','varchar(16)') as Operator
        from e.Expression.nodes('/Expression/SubExpression') as t(v)
    ) as se
)
--insert into dbo.Operands
select p.ExpressionID
    ,p.SubExpressionID
    ,d.OperandID
    ,p.Operator
    ,p.OperandType
from cte_SubExpression p
cross apply
(
    select t.v.value('@ID','int') as OperandID
        ,t.v.value('@Exclude','int') as Exclude
    from p.SubExpression.nodes('/SubExpression/Operand') as t(v)
) as d
View Code

二,使用TSQL實現邏輯與和邏輯非

數據表包含的數據是Tag和Data之間的映射關係,如果Data包含的Tag滿足邏輯表達式,那麼就認為Data和邏輯表達式(Expression)之間存在映射關係,也就是說,Data滿足Expression。

例如,有以下數據表DataTable,該表只有兩列DataID和TagID,表示Data具有相應的Tag:

create table dbo.DataTable 
(
    DataID bigint not null,
    TagID bigint not null,
)
go

insert into dbo.DataTable
(DataID,TagID)
values
(1,1)
,(1,2)
,(1,3)
,(1,7)
,(2,4)
,(2,5)
,(3,1)
,(3,2)
,(3,3)
,(4,1)
,(4,3)
,(6,6)
go
View Code

1,邏輯非的實現

邏輯非的含義是:在DataTable中,一個DataID不能包含任意一個ExcludeExpression中的TagID。

;with cte_exclude_data
as
(
    select distinct 
        dt.DataID
    from dbo.DataTable dt
    inner join dbo.Operands o
        on dt.TagID=o.OperandID
    where Operator='not'
)
select dt.DataID
    ,dt.TagID
from dbo.DataTable dt
left join cte_exclude_data ed
    on dt.DataID=ed.DataID
where ed.DataID is null

2,邏輯與的實現

 邏輯與的含義是:在DataTable中,一個DataID必須包含SubExpression中的所有Tag。

;with cte_operands as 
(
    select o.ExpressionID
        ,o.SubExpressionID
        ,o.OperandID
        ,count(0) over(partition by o.ExpressionID,o.SubExpressionID) as Operands
    from dbo.Operands o
    where o.Operator='and'
)
select o.ExpressionID
    ,o.SubExpressionID
    ,o.Operands
    ,dt.DataID
from cte_operands o
inner join dbo.DataTable dt
    on o.OperandID=dt.TagID
group by o.ExpressionID
    ,o.SubExpressionID
    ,o.Operands
    ,dt.DataID
having count(distinct dt.TagID)=o.Operands

3,查詢腳本示例 

把邏輯與和邏輯非的代碼合併到一起,就能實現表達式蘊含的邏輯,這裡給出一個示例腳本:

;with cte_operands as 
(
    select o.ExpressionID 
        ,o.SubExpressionID
        ,o.OperandID
        ,count(o.OperandID) over(partition by o.ExpressionID,o.SubExpressionID) as Operands
    from dbo.Operands o
    where Operator='and'
)
,cte_exclude_data as 
(
    select distinct 
        d.DataID
    from dbo.DataTable d
    inner join dbo.Operands o
        on d.TagID=o.OperandID 
    where o.Operator='not'
)
,cte_data as 
(
    select d.DataID
        ,d.TagID
    from dbo.DataTable d
    left join cte_exclude_data ed
        on d.DataID=ed.DataID
    where ed.DataID is null
)
select o.ExpressionID
    ,o.SubExpressionID
    ,dt.DataID
from cte_operands o
inner join cte_data dt
    on o.OperandID=dt.TagID
group by o.ExpressionID
    ,o.SubExpressionID
    ,dt.DataID
    ,o.Operands
having count(distinct dt.TagID)=o.Operands
View Code

 

附上較複雜的邏輯表達式,該格式能夠表達的邏輯組合更為豐富,後續我會分享深入探索的隨筆:

Expression = ((A & B) | (C & ! D)) & !(E | F)

本文列舉的邏輯表達式非常簡單,鑒於本人的知識和經驗有限,難免有紕漏,如果有更好的解決方案,還請不吝告知,十分感謝!

 


您的分享是我們最大的動力!

-Advertisement-
Play Games
更多相關文章
  • WebSocket是HTML5開始提供的一種單個TCP連接上進行全雙工通訊的協議。在WebSocket API中,瀏覽器和伺服器只需要做一個握手的動作,然後,瀏覽器和伺服器之間就形成了一條快速通道。兩者之間就直接可以數據相互傳送。瀏覽器通過JavaScript向伺服器發出建立WebSocket連接的 ...
  • 重新看js閉包的時候看到了《大部分人都會做錯的經典JS閉包面試題》,自己理解並記錄了下想法。很多部分博主已經講得很詳細了,只是後面的解釋部分文字有點繞。 原帖地址:http://web.jobbole.com/84328/ 先貼代碼 問:每一個輸出分別是什麼? 答案: 來逐步拆解: ...
  • 前言: 項目開發中遇到了需要將HTML頁面的內容導出為一個word文檔,所以有了這邊隨筆。 當然,項目開發又時間有點緊迫,第一時間想到的是用插件,所以百度了下。下麵就介紹兩個導出word文檔的方法。 法一:通過jquery.wordexport.js導出word 備註:相容IE9以上 大概瀏覽了下j ...
  • Xcode導出App一般問題及其解決方法 問題一:開發者協議變更問題。 變更後打包會出現如下圖A警告,此時點擊 ”visit developer website“進入Apple開發者網站,登錄開發者賬號後會出現如圖B後,點擊“Review Agreement” 進入下一步確認同意條款即可。 (圖A) ...
  • Android精選源碼 Android優質博客 Kotlin編程中使用Glide v4 Generated API前期準備:在Gralde中引用Glide庫:dependencies {compile fileTree(include: , dir: 'libs')androidTestCompil ...
  • 前文回顧: 上篇博客講到GCD的實現是由隊列和任務兩部分組成,其中獲取隊列的方式有兩種,第一種是通過GCD的API的dispatch_queue_create函數生成Dispatch Queue;第二種是直接使用系統提供的標準Dispatch Queue :Main Dispatch Queue和G ...
  • 近一年來,蘋果iOS/OS X頻繁被爆出重大安全漏洞,攻擊者可以通過漏洞竊取多達上千個應用的密碼。這些漏洞一旦被黑客掌握、利用,後果不堪設想。 好在這些漏洞的發現者還是有節操的,他們都將這些漏洞彙報給了蘋果公司,避免了重大損失的產生。不過,這也為廣大IOS用戶捏了一把冷汗。IOS並沒有想象中那麼安全 ...
  • Android十款線上工具,在做Android 開發過程中,會遇到一些小的問題,雖然自己動手也能解決,但是有了一些小工具,解決這些問題就得心應手了。Android線上工具,包括線上測試工具,及其他較為重要的描述信息產品,希望能夠幫助大家更好的完成工作,減少不必要的錯誤發生。這款工具非常好用,下麵就由 ...
一周排行
    -Advertisement-
    Play Games
  • 移動開發(一):使用.NET MAUI開發第一個安卓APP 對於工作多年的C#程式員來說,近來想嘗試開發一款安卓APP,考慮了很久最終選擇使用.NET MAUI這個微軟官方的框架來嘗試體驗開發安卓APP,畢竟是使用Visual Studio開發工具,使用起來也比較的順手,結合微軟官方的教程進行了安卓 ...
  • 前言 QuestPDF 是一個開源 .NET 庫,用於生成 PDF 文檔。使用了C# Fluent API方式可簡化開發、減少錯誤並提高工作效率。利用它可以輕鬆生成 PDF 報告、發票、導出文件等。 項目介紹 QuestPDF 是一個革命性的開源 .NET 庫,它徹底改變了我們生成 PDF 文檔的方 ...
  • 項目地址 項目後端地址: https://github.com/ZyPLJ/ZYTteeHole 項目前端頁面地址: ZyPLJ/TreeHoleVue (github.com) https://github.com/ZyPLJ/TreeHoleVue 目前項目測試訪問地址: http://tree ...
  • 話不多說,直接開乾 一.下載 1.官方鏈接下載: https://www.microsoft.com/zh-cn/sql-server/sql-server-downloads 2.在下載目錄中找到下麵這個小的安裝包 SQL2022-SSEI-Dev.exe,運行開始下載SQL server; 二. ...
  • 前言 隨著物聯網(IoT)技術的迅猛發展,MQTT(消息隊列遙測傳輸)協議憑藉其輕量級和高效性,已成為眾多物聯網應用的首選通信標準。 MQTTnet 作為一個高性能的 .NET 開源庫,為 .NET 平臺上的 MQTT 客戶端與伺服器開發提供了強大的支持。 本文將全面介紹 MQTTnet 的核心功能 ...
  • Serilog支持多種接收器用於日誌存儲,增強器用於添加屬性,LogContext管理動態屬性,支持多種輸出格式包括純文本、JSON及ExpressionTemplate。還提供了自定義格式化選項,適用於不同需求。 ...
  • 目錄簡介獲取 HTML 文檔解析 HTML 文檔測試參考文章 簡介 動態內容網站使用 JavaScript 腳本動態檢索和渲染數據,爬取信息時需要模擬瀏覽器行為,否則獲取到的源碼基本是空的。 本文使用的爬取步驟如下: 使用 Selenium 獲取渲染後的 HTML 文檔 使用 HtmlAgility ...
  • 1.前言 什麼是熱更新 游戲或者軟體更新時,無需重新下載客戶端進行安裝,而是在應用程式啟動的情況下,在內部進行資源或者代碼更新 Unity目前常用熱更新解決方案 HybridCLR,Xlua,ILRuntime等 Unity目前常用資源管理解決方案 AssetBundles,Addressable, ...
  • 本文章主要是在C# ASP.NET Core Web API框架實現向手機發送驗證碼簡訊功能。這裡我選擇是一個互億無線簡訊驗證碼平臺,其實像阿裡雲,騰訊雲上面也可以。 首先我們先去 互億無線 https://www.ihuyi.com/api/sms.html 去註冊一個賬號 註冊完成賬號後,它會送 ...
  • 通過以下方式可以高效,並保證數據同步的可靠性 1.API設計 使用RESTful設計,確保API端點明確,並使用適當的HTTP方法(如POST用於創建,PUT用於更新)。 設計清晰的請求和響應模型,以確保客戶端能夠理解預期格式。 2.數據驗證 在伺服器端進行嚴格的數據驗證,確保接收到的數據符合預期格 ...