none
請教一個SQL條件 查詢問題 RRS feed

  • 問題

  •  

    如果我想實現以下的語法

    if 條件一成立, then 查詢一

    if 條件二成立, then 查詢二

    if 條件三成立, then 查詢三

     

    還有, 只有條件成立才查詢

    但最後結果為查詢結果的交集

     

    另外,這只是在一個table上查詢不同欄位

     

     

    不知有沒有方法用SQL上實現呢?

     

     

     

     

    2008年10月22日 上午 03:19

解答

  • Code Snippet

    CREATE PROCEDURE dbo.test AS
    DECLARE @StrSqlCmd varchar;
    DECLARE @StrSqlWhere varchar;
    DECLARE @StrSqlAnd varchar;
    DECLARE @FindUserName varchar;
    DECLARE @FindTel varchar;

    set @StrSqlCmd ='select * from MyUserTable';
    set @StrSqlWhere ='';
    set @StrSqlAnd ='';

    if (@FindUserName is not null)
    begin
    set @StrSqlWhere = @StrSqlWhere + @StrSqlAnd + 'UserName like ''' + replace(@FindUserName,'''','''''') +'%''';
    set @StrSqlAnd =' and ';
    end

    if (@FindTel is not null)
    begin
    set @StrSqlWhere = @StrSqlWhere + @StrSqlAnd + 'Tel = ''' + replace(@FindTel,'''','''''') +'''';
    set @StrSqlAnd =' and ';
    end

    if (len(@StrSqlWhere) > 0)
    begin
    set @StrSqlCmd = @StrSqlCmd + ' where ' + @StrSqlWhere;
    end

    EXEC sp_executesql @StrSqlCmd;

     

     

    先感謝大大的回覆

    今日試過了, 可以運行,

    但是想問,假如查詢是數字(int)的話,那怎麼辦呢?

     

     

    2008年10月23日 上午 04:57
  • 我忘了
    SQL 不支援字串
    進行串接時
    自動把非字串的變數
    自動轉換成字串

    (有時候學太多東西, 觀念有時會彼此混淆)

    你改用這樣看看, 在串接之串前, 先用 convert() 去轉換成字串

    Code Snippet


    DECLARE @No Int;


    if (@No <> 0)

    begin
    set @StrSqlWhere = @StrSqlWhere + @StrSqlAnd + 'No = ' + convert(nvarchar,@No);
    set @StrSqlAnd =' and ';

    end




    2008年10月24日 上午 01:53
  • 1.你用字串把轉過的日期包起來看看
     'DateBook = '''+ convert(nvarchar,@DateBook,111) + ''''

    或是
    2.你查一下, 是不是DateBook除了日期以外, 還有時間值?
    如果有時間的話(非 00:00:00), 要用區間範圍的查詢方式, 像是下面這樣

     'DateBook >= '''+ convert(nvarchar,@DateBook,111) + ''' and DateBook < ''' + convert(nvarchar,dateadd(dd,1,@DateBook),111) + ''''

    或是把傳入的時間值, 先拆成3個int的變數

    set @DateBookYear = year(@DateBook);
    set @DateBookMonth = Month(@DateBook);
    set @DateBookDay = Day(@DateBook);

     'Year(DateBook) = '+ convert(nvarchar,@DateBookYear) + ' and  Month(DateBook) = '+ convert(nvarchar,@DateBookMonth) + ' and  Day(DateBook) = '+ convert(nvarchar,@DateBookDay)


    或是你有其他更好的寫法?
    大概就這樣了...
    2008年10月24日 上午 04:22

所有回覆

  • Code Snippet
    DECLARE @StrSqlCmd nvarchar(max);
    DECLARE @StrSqlWhere nvarchar(max);
    DECLARE @StrSqlAnd nvarchar(5);

    set @StrSqlCmd ='select * from MyUserTable';
    set @StrSqlWhere ='';
    set @StrSqlAnd ='';

    if (@FindUserName is not null)
    begin
    set @StrSqlWhere = @StrSqlWhere + @StrSqlAnd + 'UserName like ''' + replace(@FindName,'''','''''') +'%''';
    set @StrSqlAnd =' and ';
    end

    if (@FindTel is not null)
    begin
    set @StrSqlWhere = @StrSqlWhere + @StrSqlAnd + 'Tel = ''' + replace(@FindTel,'''','''''') +'''';
    set @StrSqlAnd =' and ';
    end

    if (len(@StrSqlWhere) > 0)
    begin
    set @StrSqlCmd = @StrSqlCmd + ' where ' + @StrSqlWhere;
    end

    EXEC sp_executesql @StrSqlCmd;


    大概像是這樣吧?

    ---------------
    請注意
    sp_executesql 必須使用 nvarchar 當傳入值

    以及
    MS-SQL的if 後面不用 then (then 是 case when (判斷式) then 在用的)
    像是

    if (判斷式)
    執行式;

    或是

    if (判斷式)
    執行式;
    else
    執行式(否則用);

    如果不是單行, 再用 begin 與 end 包起來

    ------------
    Orace的if才需要用到 then 與 end if;
    2008年10月22日 上午 09:28
  • 這個方法好像在SQL server 用不到吧

    我想是在SQL server上實現的

    2008年10月22日 上午 10:34
  • 會這樣講,表示你根本沒有試 ...

    EXEC sp_executesql 是 SQL Server 中的一個預存程序。

     

    2008年10月22日 上午 10:40
    版主
  • 請包成MS-SQL Server的預存程序來用
    其他的你自己先試
    請記得預存程序的傳入參數要能夠接受 null
    這樣條件式才會正常作用

    --------------------
    昨天
    我家那邊的社區
    不知道是誰在發神經
    半夜2點左右觸發了社區的火災警報器
    響了快半小時

    早上出門前再跟家人搶馬桶(水便)
    結果再度遲到
    今天又拉肚子在公司內跑了好幾次WC
    到下午開始痛了

    那個原理應該沒有錯
    但我還沒實測過
    準備過幾天改寫網頁時
    把原本放在 aspx.cs 內組SQL字串的那一大串
    都搬到MS-SQL預存程序去

    先去藥房買
    "安腸平錠"了

    如果你寫成像我提供的範例那樣
    還有問題的話
    請提供你的錯誤訊息
    我們再來一起研究

    2008年10月22日 上午 10:43
  • Code Snippet

    CREATE PROCEDURE dbo.test AS
    DECLARE @StrSqlCmd varchar;
    DECLARE @StrSqlWhere varchar;
    DECLARE @StrSqlAnd varchar;
    DECLARE @FindUserName varchar;
    DECLARE @FindTel varchar;

    set @StrSqlCmd ='select * from MyUserTable';
    set @StrSqlWhere ='';
    set @StrSqlAnd ='';

    if (@FindUserName is not null)
    begin
    set @StrSqlWhere = @StrSqlWhere + @StrSqlAnd + 'UserName like ''' + replace(@FindUserName,'''','''''') +'%''';
    set @StrSqlAnd =' and ';
    end

    if (@FindTel is not null)
    begin
    set @StrSqlWhere = @StrSqlWhere + @StrSqlAnd + 'Tel = ''' + replace(@FindTel,'''','''''') +'''';
    set @StrSqlAnd =' and ';
    end

    if (len(@StrSqlWhere) > 0)
    begin
    set @StrSqlCmd = @StrSqlCmd + ' where ' + @StrSqlWhere;
    end

    EXEC sp_executesql @StrSqlCmd;

     

     

    先感謝大大的回覆

    今日試過了, 可以運行,

    但是想問,假如查詢是數字(int)的話,那怎麼辦呢?

     

     

    2008年10月23日 上午 04:57
  • 照你的寫法,如果我在 @FindTel 中下一個指令:

     

    Code Snippet

    SET @FindTel = ' '''; DROP DATABASE master; GO''''

     

     

    你認為會怎麼樣呢?

    (此指令有安全問題,不可亂試,亂試的結果自行負責...)

    2008年10月23日 上午 05:55
    版主
  •  

    那麼你應該認為怎樣寫呢?

    還有如果是數字或者其它格式,那怎麼辦?

    2008年10月23日 上午 06:30
  •  

    多寫幾隻SP,然後由前端去判斷條件,然後去呼叫相對應條件的SP就好了.
    2008年10月23日 上午 08:19
  • 1.如果怕有安全性問題的話, 請勿用sa帳號執行預存程序, 另外開別的User, 預先把可能會有安全性顧慮的權限鎖住
    2.照其原理, 用replace把一個單引號取代為2個單引號, 在組SQL字串時, 應該可以避開SQL資料隱碼才是
    3.在預存程序裡, 去組SQL字串再執行, 非我所原創, 數年前就有人以此方式去做在預存程序裡, 做分頁Select了

    你的set @FindTel
    本來就斷成2個sql命令了

    正常帶入的字串
    不會長那樣

    還有
    請記得在建預存程序時, 在前面
    加上


    SET ANSI_NULLS ON
    GO
    SET QUOTED_IDENTIFIER ON
    GO
    -----------------
    replace(@FindTel,'''','''''')

    第2個參數是連續4個單引號 = 被字串包起來的1個單引號字元
    第3個參數是連續6個單引號= 被字串包起來的2個單引號字元

    這樣應該沒錯吧?




    2008年10月23日 上午 10:43
  • 以ASP.NET的SqlDataSource 去selete MS-SQL時
    通常空字串 "" 會變成 null

    但是 int 或其他的資料型態
    你要自己決定預設值為何
    這樣你在的預存程序裡才方便判斷

    例如連續數字(例如1~12月)的下拉式選單, 多個 -1 的值, 顯示為空字串
    這樣你就可以在預存程序裡判斷是否傳入為-1
    來判斷User是否下拉過該選單了

                        <asp:ControlParameter ControlID="DropDownList1" Name="ID" PropertyName="SelectedValue"
                            Type="String" ConvertEmptyStringToNull="False" />

    在VS 2005時
    要開
    ConvertEmptyStringToNull="False"

    這樣才會把空字串轉null ?

    試到頭腦不清了?
    2008年10月23日 上午 10:48
  • set @FindTel =''';select @@version;--'

    請改用像是這種的方法來測
    在SQL Server Management Studio裡
    先測過你的SQL預存程序
    有沒有SQL資料隱碼的漏洞後
    再放到實際要跑的環境中
    以防萬一

    請勿開玩笑使用高危險性的SQL命令
    2008年10月23日 上午 11:56
  • int的話
    如果你在預存程序一開始
    把傳入參數定義為int的話
    記得MS-SQL應該不會讓你傳字串進去
    所以應該不會有SQL資料隱碼的問題

    你可以直接用像是這樣, 但如果是字串的話, 請參考上述字串的串接方式, 以防萬一

     if (@No <> 0)
    begin
    set @StrSqlWhere = @StrSqlWhere + @StrSqlAnd + 'No = ' + @No;
    set @StrSqlAnd =' and ';

    end
    2008年10月23日 下午 12:30
  • 是否這樣?

    Code Snippet

    DECLARE @No Int;

    if (@No <> 0)
    begin
    set @StrSqlWhere = @StrSqlWhere + @StrSqlAnd + 'No = ' + @No;
    set @StrSqlAnd =' and ';

    end

     

     

    但我試過後

    在主程序出現了這個錯誤訊息

    Procedure or function XXXXXXX has too many arguments specified.

    2008年10月24日 上午 01:34
  • 我忘了
    SQL 不支援字串
    進行串接時
    自動把非字串的變數
    自動轉換成字串

    (有時候學太多東西, 觀念有時會彼此混淆)

    你改用這樣看看, 在串接之串前, 先用 convert() 去轉換成字串

    Code Snippet


    DECLARE @No Int;


    if (@No <> 0)

    begin
    set @StrSqlWhere = @StrSqlWhere + @StrSqlAnd + 'No = ' + convert(nvarchar,@No);
    set @StrSqlAnd =' and ';

    end




    2008年10月24日 上午 01:53
  •  

    現在可以正常運行了...先謝謝,

    但現在還有一個問題...

    假如查詢類型為DateTime, 我試過用convert(nvarchar,@DateBook)

    但它在主程式就轉不到...(在主程式好像只讀到日子)

     

    2008年10月24日 上午 02:19
  • DateTime 如果要轉字串
    你要指定要轉成哪種格式的

    例如
    convert(nvarchar,@DateBook,111)

    yyyy/mm/dd 這種只有日期, 去掉時間的字串

    你可以參考
    http://msdn.microsoft.com/zh-tw/library/ms187928.aspx
    2008年10月24日 上午 02:25
  •  player555767 寫信:
    DateTime 如果要轉字串
    你要指定要轉成哪種格式的

    例如
    convert(nvarchar,@DateBook,111)

    yyyy/mm/dd 這種只有日期, 去掉時間的字串

    你可以參考
    http://msdn.microsoft.com/zh-tw/library/ms187928.aspx

     

    現在可以讀到,但就是沒有記錄(明明是有的),我試過直接入日期查詢,都是查詢不到...

     

     'DateBook = '+ convert(nvarchar,'21/10/2008',111)

    2008年10月24日 上午 04:09
  • 1.你用字串把轉過的日期包起來看看
     'DateBook = '''+ convert(nvarchar,@DateBook,111) + ''''

    或是
    2.你查一下, 是不是DateBook除了日期以外, 還有時間值?
    如果有時間的話(非 00:00:00), 要用區間範圍的查詢方式, 像是下面這樣

     'DateBook >= '''+ convert(nvarchar,@DateBook,111) + ''' and DateBook < ''' + convert(nvarchar,dateadd(dd,1,@DateBook),111) + ''''

    或是把傳入的時間值, 先拆成3個int的變數

    set @DateBookYear = year(@DateBook);
    set @DateBookMonth = Month(@DateBook);
    set @DateBookDay = Day(@DateBook);

     'Year(DateBook) = '+ convert(nvarchar,@DateBookYear) + ' and  Month(DateBook) = '+ convert(nvarchar,@DateBookMonth) + ' and  Day(DateBook) = '+ convert(nvarchar,@DateBookDay)


    或是你有其他更好的寫法?
    大概就這樣了...
    2008年10月24日 上午 04:22
  •  player555767 寫信:
    1.你用字串把轉過的日期包起來看看
     'DateBook = '''+ convert(nvarchar,@DateBook,111) + ''''

    或是
    2.你查一下, 是不是DateBook除了日期以外, 還有時間值?
    如果有時間的話(非 00:00:00), 要用區間範圍的查詢方式, 像是下面這樣

     'DateBook >= '''+ convert(nvarchar,@DateBook,111) + ''' and DateBook < ''' + convert(nvarchar,dateadd(dd,1,@DateBook),111) + ''''

    或是把傳入的時間值, 先拆成3個int的變數

    set @DateBookYear = year(@DateBook);
    set @DateBookMonth = Month(@DateBook);
    set @DateBookDay = Day(@DateBook);

     'Year(DateBook) = '+ convert(nvarchar,@DateBookYear) + ' and  Month(DateBook) = '+ convert(nvarchar,@DateBookMonth) + ' and  Day(DateBook) = '+ convert(nvarchar,@DateBookDay)


    或是你有其他更好的寫法?
    大概就這樣了...

     

    我用了你的第二種方法, 終於成功了,真是太感激大大的回覆

     

    提外話, 可以有更優的方法嗎?, 在另一方面, 好像有存在一些安全漏洞的問題,可以更好地解決?

    2008年10月24日 上午 07:07
  • 自組SQL命令字串, 可能導致SQL資料隱馬的漏洞
    但是
    請注意
    凡是傳入字串的參數時
    請務必將單引號取代為連續2個單引號
    以便迴避資料隱碼的問題

    你的問題或許有更好的解法
    但我現在
    什麼都想不出來了

    --
    下午胃腸痛
    呼吸不順
    頭腦一片空白
    (自己之前趕工寫的網頁, 耦合度太高, 1個頁面裡, 拉了2個GridView與1個FormView, 使用2種模式去操作
    A.URL有年月日, GridView A顯示指定日期主檔資訊,
    GridView B顯示指定日期的明細, 在點擊GridView B的資料列後, 在顯示到FormView
    B.URL無日期,
    GridView A隱藏不顯示,GridView B顯示無法以日期歸檔的明細, 在點擊GridView B的資料列後, 在顯示到FormView
    原本是想
    GridView B與FormView可以2個操作流程共用, 才寫在一起的
    可是,周三下午接到新的需求, 操作流程又要改了, B的GridView B上面要接上搜尋條件, 還有另一頁要連進這一頁的FormView
    原本昨晚之前, 還想得應該OK可以改得出來
    結果半夜胃腸痛, 今天下午又再痛
    結果還沒寫在筆記本上, 原本想好的流程控制, 全部被胃腸痛的記憶擠出腦中了, 現在不知道
    該做什麼了

    該把
    FormView再抽出來寫成控制項嗎? 可是頁面內互傳的變數太多了, 包含之前嘔心瀝血寫的"回上一頁"機制
    )
    2008年10月24日 上午 07:31