2010-01-17 84 views
3

如何將此查詢轉換爲內聯sql或存儲過程?如何將此linq代碼轉換爲內聯sql

var a = from arow in context.post 
where arow.post_id == id && arow.post_isdeleted == false 
select new 
{ 
    arow.post_id, 
    PostComments = from c in context.comment 
        where c.CommentPostID == arow.post_id 
        select new 
        { 
         c.id, 
         c.title  
        } 
} 


List<PostType> pt; 
foreach (var s in a) 
{ 
    pt = new PostType(); 
    pt.PostID = s.post_id; 

    //how would I use ADO.NET to put this in a custom class? 
    foreach(var ctl in s.PostComments) 
    { 
     ctl.Title = ctl.title; 
     pt.CommentT.Add(ctl); 
    } 
    ptl.Add(pt); 
} 

一旦執行了內聯查詢,我會如何將信息放入自定義類中? PostComments是一個子查詢 - 那麼如何使用ADO.NET將其放入自定義類中?

+0

你正在使用什麼SQL?如果你使用t-sql,答案將不會像你使用plsql一樣。 – ALOToverflow 2010-01-20 17:31:07

+0

我正在使用sql server – Luke101 2010-01-20 17:48:17

回答

1

簡短說明

加載它在列表看來部分你的問題可能看起來比較棘手,那就是如何以與LINQ to SQL(從此處出來的「L2S」)查詢爲匿名類一樣的方式填充自定義類。

根據您foreach循環我猜你的自定義類是類似於這些:

public class PostType 
{ 
    public int PostId { get; set; } 
    public List<PostComment> PostComments { get; set; } 
} 
public class PostComment 
{ 
    public int CommentId { get; set; } 
    public string Title { get; set; } 
} 

LINQ查詢應相當於該T-SQL語句:

SELECT P.post_id, C.id, C.title 
FROM post As P, comment As C 
WHERE 
    P.post_id = @PostId 
    AND P.post_isdeleted = 0 -- 0 is false 
    AND C.CommentPostID = P.post_id 

不像L2S版本(請參閱下面的詳細解釋部分以獲取更多信息),則此語句將返回一個展平結果,每行包含一個P.post_id,C.idC.title。如果你的類以相同的方式代表一個入口,這將很容易解決(我不主張這樣的設計;我只是在評論設計如何改變它如何填充)。類中的層次關係改變了事物。

此外,您的代碼顯示List<PostType>,但不需要列表,因爲總是會有一個PostType,因爲您在post_id上進行篩選。如果該條件被刪除,則,然後您可能會在滿足其他條件的情況下獲得與具有不同PostIds的多個匹配項。如果是這種情況,下面的代碼將需要更改。

也就是說,讓我們跳入一些ADO.NET並使用SqlDataReader填充類。

int postIdInput = 42; // desired post_id to search for 

// PostType delcared prior to getting the results 
PostType postType = new PostType() 
{ 
    PostId = postIdInput, 
    PostComments = new List<PostComment>() 
}; 

// Database interaction starts here... 
// updated SQL statement to use column name aliases for clarity when used by the SqlDataReader 
string sqlStatement = @"SELECT P.post_id As PostId, C.id As CommentId, C.title As Title 
         FROM post As P, comment As C 
         WHERE 
          P.post_id = @PostId 
          AND P.post_isdeleted = 0 -- 0 is false 
          AND C.CommentPostID = P.post_id"; 

string sqlConnectionString = "..."; // whatever your connection is... probably identical to your L2S context.Connection.ConnectionString 
using (SqlConnection conn = new SqlConnection(sqlConnectionString)) 
{ 
    conn.Open(); 
    SqlCommand command = new SqlCommand(sqlStatement, conn); 
    command.Parameters.AddWithValue("@PostId", postIdInput); // use Parameters.Add() for greater specificity 

    SqlDataReader reader = command.ExecuteReader(); 
    while (reader.Read()) 
    { 
     // postId was set based on input, but could be set here as well although it would occur repeatedly 
     // if desired, uncomment the 2 lines below and there's no need to initialize it earlier (it'll be overwritten anyway) 
     //int postId = Int32.Parse(reader["PostId"].ToString()); 
     //postType.PostId = postId; 
     int commentId = Int32.Parse(reader["CommentId"].ToString()); 
     string title = reader["Title"].ToString(); 

     // add new PostComment to the list 
     PostComment postComment = new PostComment 
     { 
      CommentId = commentId, 
      Title = title 
     }; 
     postType.PostComments.Add(postComment); 
    } 

    // done! postType is populated... 
} 

// use postType... 

這應該涵蓋您的方案。但是,要獲得更詳細的答案,請繼續閱讀!


詳解(又名「授人以漁。」

比方說,你無法弄清楚如何獲得等價的SQL語句。儘管有不同的方式可以這樣做,但我會專注於您正在使用L2S並探索一些相關選項的事實。

第1步:轉換LINQ查詢到SQL (通過 「欺騙」)

你很幸運,因爲有一個快捷方式。將現有的LINQ表達式轉換爲SQL比向後翻譯SQL和LINQ更方便。

您可以通過使用這些DataContext的選項從您的代碼轉換的T-SQL語句:

注:我沒有說這是一條捷徑。 SQL的知識很好理解,並且要清楚我並不是建議盲目使用生成的輸出。當然,SQL可能與您期望的有所不同,但它提供了一個體面的起點。如果需要,你可以調整它。

使用這些方法中的任何一種並複製結果 - 您將需要它的步驟2

實施例DataContext.GetCommand()的用法:

var query = /* your L2S query here */; 
string sqlStatement = context.GetCommand(query).CommandText; // voila! 

要獲得的結果要麼設置斷點和複製其值,檢查出來在立即窗口,或某處顯示它(Console.WriteLine等等。)。

例DataContext.Log用法:在這種情況下執行將轉儲到控制檯窗口的SQL語句

context.Log = Console.Out; 

查詢。你可以從那裏複製它。在其他地方把它們扔掉,比如到調試輸出窗口,請查看以下鏈接:

第2步:在手的SQL語句,在ADO.NET中使用它

現在您已經擁有了SQL語句,我們可以在ADO.NET中使用它。當然你也可以使用存儲過程,並且不應該很難替代它。

在使用它之前,您可能需要清理聲明。我用了一個類似的查詢本地得到這個和你生成的語句可能類似於此:

SELECT [t0].[post_id], [t1].[id], [t1].[title], (
SELECT COUNT(*) 
FROM [comment] AS [t2] 
WHERE [t2].[id] = [t0].[post_id] 
) As [value] 
FROM [post] As [t0] 
LEFT OUTER JOIN [comment] As [t1] ON [t1].[CommentPostID] = [t0].[post_id] 
WHERE ([t0].[post_id] = @p0) AND ([t0].[post_isdeleted] = 0) 
ORDER BY [t0].[post_id], [t1].[id] 

通知嵌入式SELECT COUNT(*)? L2S查詢從未請求計數,但結果請求連接上使用的相同ID的計數。另請注意,這些列沒有別名。你會根據它們的實際名稱來參考這些列(即post_idPostId)。另外,SQL參數被命名爲@ p0 ... @ pn,並且應用默認排序順序。您可以將其複製/粘貼到之前使用的SqlDataReader中,但您需要重命名要匹配的列和參數。

一個清理的上述版本與註釋掉重命名的參數和不必要的部分轉載如下(如果這種方法是採取測試,以確保它相當於預計的):

SELECT [P].[post_id] As PostId, [C].[id] As CommentId, [C].[title] As Title--, (
-- SELECT COUNT(*) 
-- FROM [comment] AS [t2] 
-- WHERE [t2].[id] = [t0].[post_id] 
--) As [value] 
FROM [post] As [P] 
LEFT OUTER JOIN [comment] As [C] ON [C].[CommentPostID] = [P].[post_id] 
WHERE ([P].[post_id] = @PostId) AND ([P].[post_isdeleted] = 0) 
--ORDER BY [t0].[post_id], [t1].[id] 

以上現在可以與之前的SqlDataReader一起使用。

如果L2S查詢是在一個SelectMany的格式,如可能已經產生更直接的查詢:

var query = from arow in context.post 
      from c in context.comment 
      where arow.post_id == id && arow.post_isdeleted == false 
        && c.CommentPostID == arow.post_id 
      select new 
      { 
       arow.post_id, 
       c.id, 
       c.title  
      }; 

的L2S的SelectMany查詢生成類似下面的SQL語句:

SELECT [t0].[post_id], [t1].[id], [t1].[title] 
FROM [post] As [t0], [comment] As [t1] 
WHERE ([t0].[post_id] = @p0) AND ([t0].[post_isdeleted] = 0) 
     AND ([t1].[CommentPostID] = [t0].[post_id]) 

LINQPad

雖然這個詳細的解釋看起來就非常困難,有一個輕鬆掌握這些信息。如果你還沒有給LINQPad一個嘗試,那麼我強烈推薦它 - 它也是免費的! LINQPad將向您顯示您的L2S查詢結果,具有查看生成的SQL的SQL選項卡,並顯示使用的lambda表達式(上述查詢語法顯示爲等效的lambda/extension)。最重要的是,它是用於C#/ VB.NET(包括LINQ to Objects/XML)的通用工具,以及具有數據庫支持的SQL編碼等等。

這裏是LINQPad的一個微小的屏幕截圖顯示了一些前面討論的主題:

LINQPad

我不想佔用更多的頁面空間比我已經有這麼click here to see the image in its original size


如果您到此爲止,恭喜!:)

+0

這只是真實的..不知道多少要謝謝你。我希望我可以多次投票..謝謝你在這樣詳細的解釋..我已經下載linqpad,它幫助巨大。我希望我能聘請你爲我的項目。再次感謝 – Luke101 2010-01-21 08:12:07

+0

@ Luke101很高興幫助和感謝:) – 2010-01-21 14:42:20

1

如果你的意思是有郵電PostComments表之間的關係,並有兩個表中重複列和一個註釋可能與一個以上的職位,以便 您可以輕鬆創建兩個命令:

-Select * from Posts where post_Id = id AND IsDeleted = 0; 
-Select * from Postcomments where id = cid; 

,然後在兩個數據表上使用Sql命令適配器執行它們。然後:

foreach(DataRow dr in PostsTable.Rows) 
{ 
//Fill the Post Custom class 
SecondTable.DefaultView.RowFilter = string.Format("PostID = {0}",dr["postID"]); 
foreach(DataRow r in SecondTable.Rows) 
{ 
    //Fill the Comments Custom class 
} 
} 

如果這不是你的情況,所以你可以嘗試澄清你的數據庫結構?

+0

對不起。我編輯了查詢。是的,PostComments與每篇文章(Post)相關。類似於對SO的評論或相關答案。 – Luke101 2010-01-17 07:46:17

+0

如果你只獲得一篇文章及其所有評論,所以你只能創建一個命令並且同時執行兩條sql語句,而使用sql數據讀取器而不是方法ReadNextResult()來填充註釋。 – 2010-01-17 09:43:44

0

我不能沿着線測試這一點,但東西:

SELECT 
    p.post_id 
    c.id, 
    c.title 
FROM 
    post p 
WHERE 
    p.id == 'id' and 
    isdeleted = false 
INNER JOIN comment c ON c.commentpostid = p.post_id 

我利用了可讀性關鍵字,但對於DBS你使用,你可能需要改變這種狀況。

1

使用SQL事件探查器來捕獲生成的查詢。複製到新的存儲過程並修復輸入參數。創建(保存),並用它:)

0
select post_id, id, title from postcomments pc 
where post_id = @id and exists(
    select post_id form post p where p.post_id = pc.post_id and isdeleted = false 
) 

使用DataReader獲取數據&只是你的自定義類