本文转自:http://msdn.microsoft.com/zh-cn/library/hh225374.aspx
查询表达式可以查询数据源并将数据是一种预期形式。 查询表达式提供 F# 中支持 LINQ。
query { expression }
查询表达式是计算表达式的类型类似于序列表达式。 就象通过提供代码指定顺序在序列表达式中,可以在查询表达式指定设置数据通过提供代码。 在序列表达式,yield 关键字标识要返回的数据为产生的序列的一部分。 在查询表达式中,select 关键字执行相同的功能。 除了 select 关键字以外,F# 还支持十分类似于 SQL SELECT 语句的大量查询运算符。 这是一个简单的查询表达式的示例,与连接到 Northwind OData 源的代码。
// Use the OData type provider to create types that can be used to access the Northwind database.
// Add References to FSharp.Data.TypeProviders and System.Data.Services.Client open Microsoft.FSharp.Data.TypeProviders type Northwind = ODataService<"http://services.odata.org/Northwind/Northwind.svc"> let db = Northwind.GetDataContext() // A query expression. let query1 = query { for customer in db.Customers do select customer } query1 |> Seq.iter (fun customer -> printfn "Company: %s Contact: %s" customer.CompanyName customer.ContactName)
在前面的代码示例中,查询表达式在大括号。 代码的含义在表达式的是,返回每个客户在 customers 表在查询结果的数据库中。 查询表达式返回实现 IQueryable<T> 和 IEnumerable<T>的类型,并且,因此可重复使用 序列模块,当此示例显示。
每个计算表达式类型从生成器选件类生成。 查询计算表达式的生成器选件类是 QueryBuilder。 有关更多信息,请参见计算表达式 (F#)和Linq.QueryBuilder 类 (F#)。
查询运算符在要返回的记录可以指定查询的详细信息,例如放置 standard 或指定结果排序顺序。 查询源必须支持查询运算符。 如果尝试使用不支持的查询运算符,NotSupportedException 将引发异常。
可以转换为 SQL 只的表达式在查询表达式中允许的。 例如,那么,当您使用 where 查询运算符时,在表达式不允许函数调用。
表 1 显示可用的查询运算符。 此外,请参见 Table2 后,比较 SQL 查询和等效的 F# 查询表达式本主题。 某些查询运算符不是由某些类型提供程序支持。 具体而言,OData 类型提供程序在查询运算符绑定到 OData 支持由于限制。 有关更多信息,请参见 ODataService 类型提供程序 (f#)。
此表假定一个数据库为以下形式:
代码还在下表中假定下列数据库连接代码。 项目应添加对 System.Data、要求和 FSharp.Data.TypeProviders 程序集。 创建此数据库的代码是组成的本主题末尾的。
open System
open Microsoft.FSharp.Data.TypeProviders open System.Data.Linq.SqlClient open System.Linq open Microsoft.FSharp.Linq type schema = SqlDataConnection<"Data Source=SERVER\INSTANCE;Initial Catalog=MyDatabase;Integrated Security=SSPI;"> let db = schema.GetDataContext() // Needed for some query operator examples: let data = [ 1; 5; 7; 11; 18; 21]
运算符 |
描述 |
contains |
确定所选元素是否包含一个指定的元素。 |
count |
返回所选元素的数量。 |
last |
到目前为止选择选定的表的最后一个元素。 |
lastOrDefault |
;如果未找到至此,选择选定的表中的最后一个元素或一个默认值元素。 |
exactlyOne |
到目前为止选定的一个,特定元素。 如果多个元素存在,则将引发。 |
exactlyOneOrDefault |
;如果未找到至此,选择选定的表的唯一,特定元素或一个默认值该元素。 |
headOrDefault |
如果该序列不包含元素至此,选择选定的表的第一个元素或一个默认值。 |
select |
到目前为止项目选定的每个元素。 |
where |
根据指定谓词的元素。 |
minBy |
为选定的每个元素到目前为止选择一个值并返回最小的结果值。 |
maxBy |
为选定的每个元素到目前为止选择一个值并返回最大结果值。 |
groupBy |
到目前为止组按照指定的键选择器中选择的元素。 |
sortBy |
到目前为止对升序选择组件的特定排序的键。 |
sortByDescending |
到目前为止按降序进行选择的元素由特定排序的键。 |
thenBy |
在升序到目前为止执行特定排序的键选择哪个后续排序元素。 此运算符只能在 sortBy、sortByDescending、thenBy或 thenByDescending之后只使用。 |
thenByDescending |
按降序到目前为止执行特定排序的键选择哪个后续排序元素。 此运算符只能在 sortBy、sortByDescending、thenBy或 thenByDescending之后只使用。 |
groupValBy |
为给定键选择和组至此选择一个值元素的每个元素。 |
join |
相互关系两个基于匹配的键的设置选定的值。 请注意键的顺序在周围的 = 签名连接表达式是有意义的。 在连接,因此,如果行。-> 符号后拆分,缩进的缩进距离必须至少在关键字 for。 |
groupJoin |
相互关系两个基于匹配的键和组的设置选定的值结果。 请注意键的顺序在周围的 = 签名连接表达式是有意义的。 |
leftOuterJoin |
相互关系两个基于匹配的键和组的设置选定的值结果。 如果任何组为空,使用一组具有一个默认值。 请注意键的顺序在周围的 = 签名连接表达式是有意义的。 |
sumByNullable |
为选定的每个元素到目前为止选择一个可以为 null 的值并返回这些值的总和。 如果有可以为 null,可不具有值,它将被忽略。 |
minByNullable |
为选定的每个元素到目前为止选择一个可以为 null 的值并返回这些值最小位数。 如果有可以为 null,可不具有值,它将被忽略。 |
maxByNullable |
为选定的每个元素到目前为止选择一个可以为 null 的值并返回这些值最大次数。 如果有可以为 null,可不具有值,它将被忽略。 |
averageByNullable |
为选定的每个元素到目前为止选择一个可以为 null 的值并返回平均值这些值。 如果有可以为 null,可不具有值,它将被忽略。 |
averageBy |
为选定的每个元素到目前为止选择一个值并返回平均值这些值。 |
distinct |
到目前为止选择不同的组件从所选元素。 |
exists |
确定选定的任何元素到目前为止是否满足条件。 |
find |
选择到目前为止满足指定条件的第一个选定的元素。 |
all |
确定选定的所有元素到目前为止是否满足条件。 |
head |
到目前为止选择第一个元素从选定的表。 |
nth |
到目前为止选择元素在选定的表中的指定索引。 |
skip |
到目前为止跳过所选元素指定数目的然后选择剩余的元素。 |
skipWhile |
在序列中的元素,只要一个指定的条件为 true 的然后选择剩余的元素。 |
sumBy |
为选定的每个元素到目前为止选择一个值并返回这些值的总和。 |
take |
到目前为止选择连续的指定数量的元素从选定的表。 |
takeWhile |
选择元素序列,只要一个指定条件为 true,则跳过剩余的元素。 |
sortByNullable |
到目前为止对升序选择的元素由特定的排序的键。 |
sortByNullableDescending |
到目前为止按降序进行选择的元素由特定的排序的键。 |
thenByNullable |
在升序到目前为止执行特定的排序的键选择哪个后续排序元素。 此运算符只能在 sortBy之后只使用、sortByDescending、thenBy或 thenByDescending,也可以为 null 的变量。 |
thenByNullableDescending |
按降序到目前为止执行特定的排序的键选择哪个后续排序元素。 此运算符只能在 sortBy之后只使用、sortByDescending、thenBy或 thenByDescending,也可以为 null 的变量。 |
下表显示了 F# 的一些常见 Transact-SQL 查询与其等效项。 代码此表还假定数据库和上表与同一初始代码相同的设置类型提供程序。
Transact-SQL (不区分大小写) |
F# 查询表达式 (区分大小写) |
---|---|
选择所有字段从表。 SELECT * FROM Student |
|
计数记录在表中。 SELECT COUNT(*) FROM Student |
|
EXISTS SELECT * FROM Student WHERE EXISTS (SELECT * FROM CourseSelection WHERE CourseSelection.StudentID = Student.StudentID) |
|
分组 SELECT Student.Age, COUNT(*) FROM Student GROUP BY Student.Age |
|
分组加上条件。 SELECT Student.Age, COUNT(*) FROM Student GROUP BY Student.Age HAVING student.Age > 10 |
|
分组的计数情况。 SELECT Student.Age, COUNT(*) FROM Student GROUP BY Student.Age HAVING COUNT(*) > 1 |
|
分组,计数、求和与求平均值计算。 SELECT Student.Age, COUNT(*), SUM(Student.Age) as total FROM Student GROUP BY Student.Age |
|
分组,计数和排序由计数。 SELECT Student.Age, COUNT(*) as myCount FROM Student GROUP BY Student.Age HAVING COUNT(*) > 1 ORDER BY COUNT(*) DESC |
|
IN 设置指定值 SELECT * FROM Student WHERE Student.StudentID IN (1, 2, 5, 10) |
|
LIKE 和 TOP。 -- '_e%' matches strings where the second character is 'e' SELECT TOP 2 * FROM Student WHERE Student.Name LIKE '_e%' |
|
与设置的模式匹配LIKE。 -- '[abc]%' matches strings where the first character is -- 'a', 'b', 'c', 'A', 'B', or 'C' SELECT * FROM Student WHERE Student.Name LIKE '[abc]%' |
|
设置了独占模式的LIKE。 -- '[^abc]%' matches strings where the first character is -- not 'a', 'b', 'c', 'A', 'B', or 'C' SELECT * FROM Student WHERE Student.Name LIKE '[^abc]%' |
|
在一个字段的LIKE,但是,选择一个不同的字段。 SELECT StudentID AS ID FROM Student WHERE Student.Name LIKE '[^abc]%' |
|
LIKE ,将子字符串搜索。 SELECT * FROM Student WHERE Student.Name like '%A%' |
|
只有两个表的简单 JOIN。 SELECT * FROM Student JOIN CourseSelection ON Student.StudentID = CourseSelection.StudentID |
|
只有 两个表的LEFT JOIN。 SELECT * FROM Student LEFT JOIN CourseSelection ON Student.StudentID = CourseSelection.StudentID |
|
JOIN 和 COUNT SELECT COUNT(*) FROM Student JOIN CourseSelection ON Student.StudentID = CourseSelection.StudentID |
|
DISTINCT SELECT DISTINCT StudentID FROM CourseSelection |
|
清单的计数。 SELECT DISTINCT COUNT(StudentID) FROM CourseSelection |
|
BETWEEN SELECT * FROM Student WHERE Student.Age BETWEEN 10 AND 15 |
|
OR SELECT * FROM Student WHERE Student.Age =11 OR Student.Age = 12 |
|
与排序的OR SELECT * FROM Student WHERE Student.Age =12 OR Student.Age = 13 ORDER BY Student.Age DESC |
|
TOP ,OR和排序。 SELECT TOP 2 student.Name FROM Student WHERE Student.Age = 11 OR Student.Age = 12 ORDER BY Student.Name DESC |
|
两个查询UNION。 SELECT * FROM Student UNION SELECT * FROM lastStudent |
|
两个查询的交集。 SELECT * FROM Student INTERSECT SELECT * FROM LastStudent |
|
CASE 情况。 SELECT student.StudentID, CASE Student.Age WHEN -1 THEN 100 ELSE Student.Age END, Student.Age from Student |
|
多个用例。 SELECT Student.StudentID, CASE Student.Age WHEN -1 THEN 100 WHEN 0 THEN 1000 ELSE Student.Age END, Student.Age FROM Student |
// Using if statement to alter results for special values.
query {
for student in db.Student do select (if student.Age.HasValue && student.Age.Value = -1 then (student.StudentID, System.Nullable<int>(100), student.Age) elif student.Age.HasValue && student.Age.Value = 0 then (student.StudentID, System.Nullable<int>(1000), student.Age) else (student.StudentID, student.Age, student.Age)) }
|
多个表。 SELECT * FROM Student, Course |
|
多联接。 SELECT Student.Name, Course.CourseName FROM Student JOIN CourseSelection ON CourseSelection.StudentID = Student.StudentID JOIN Course ON Course.CourseID = CourseSelection.CourseID |
|
多个左外部联接。 SELECT Student.Name, Course.CourseName FROM Student LEFT OUTER JOIN CourseSelection ON CourseSelection.StudentID = Student.StudentID LEFT OUTER JOIN Course ON Course.CourseID = CourseSelection.CourseID |
// Using leftOuterJoin with multiple joins.
query {
for student in db.Student do leftOuterJoin courseSelection in db.CourseSelection on (student.StudentID = courseSelection.StudentID) into g1 for courseSelection in g1.DefaultIfEmpty() do leftOuterJoin course in db.Course on (courseSelection.CourseID = course.CourseID) into g2 for course in g2.DefaultIfEmpty() do select (student.Name, course.CourseName) }
|
下面的代码可用于创建这些示例的示例数据库。
SET ANSI_NULLS ON GO SET QUOTED_IDENTIFIER ON GO USE [master]; GO IF EXISTS (SELECT * FROM sys.databases WHERE name = 'MyDatabase') DROP DATABASE MyDatabase; GO -- Create the MyDatabase database. CREATE DATABASE MyDatabase COLLATE SQL_Latin1_General_CP1_CI_AS; GO -- Specify a simple recovery model -- to keep the log growth to a minimum. ALTER DATABASE MyDatabase SET RECOVERY SIMPLE; GO USE MyDatabase; GO CREATE TABLE [dbo].[Course] ( [CourseID] INT NOT NULL, [CourseName] NVARCHAR (50) NOT NULL, PRIMARY KEY CLUSTERED ([CourseID] ASC) ); CREATE TABLE [dbo].[Student] ( [StudentID] INT NOT NULL, [Name] NVARCHAR (50) NOT NULL, [Age] INT NULL, PRIMARY KEY CLUSTERED ([StudentID] ASC) ); CREATE TABLE [dbo].[CourseSelection] ( [ID] INT NOT NULL, [StudentID] INT NOT NULL, [CourseID] INT NOT NULL, PRIMARY KEY CLUSTERED ([ID] ASC), CONSTRAINT [FK_CourseSelection_ToTable] FOREIGN KEY ([StudentID]) REFERENCES [dbo].[Student] ([StudentID]) ON DELETE NO ACTION ON UPDATE NO ACTION, CONSTRAINT [FK_CourseSelection_Course_1] FOREIGN KEY ([CourseID]) REFERENCES [dbo].[Course] ([CourseID]) ON DELETE NO ACTION ON UPDATE NO ACTION ); CREATE TABLE [dbo].[LastStudent] ( [StudentID] INT NOT NULL, [Name] NVARCHAR (50) NOT NULL, [Age] INT NULL, PRIMARY KEY CLUSTERED ([StudentID] ASC) ); -- Insert data into the tables. USE MyDatabase INSERT INTO Course (CourseID, CourseName) VALUES(1, 'Algebra I'); INSERT INTO Course (CourseID, CourseName) VALUES(2, 'Trigonometry'); INSERT INTO Course (CourseID, CourseName) VALUES(3, 'Algebra II'); INSERT INTO Course (CourseID, CourseName) VALUES(4, 'History'); INSERT INTO Course (CourseID, CourseName) VALUES(5, 'English'); INSERT INTO Course (CourseID, CourseName) VALUES(6, 'French'); INSERT INTO Course (CourseID, CourseName) VALUES(7, 'Chinese'); INSERT INTO Student (StudentID, Name, Age) VALUES(1, 'Abercrombie, Kim', 10); INSERT INTO Student (StudentID, Name, Age) VALUES(2, 'Abolrous, Hazen', 14); INSERT INTO Student (StudentID, Name, Age) VALUES(3, 'Hance, Jim', 12); INSERT INTO Student (StudentID, Name, Age) VALUES(4, 'Adams, Terry', 12); INSERT INTO Student (StudentID, Name, Age) VALUES(5, 'Hansen, Claus', 11); INSERT INTO Student (StudentID, Name, Age) VALUES(6, 'Penor, Lori', 13); INSERT INTO Student (StudentID, Name, Age) VALUES(7, 'Perham, Tom', 12); INSERT INTO Student (StudentID, Name, Age) VALUES(8, 'Peng, Yun-Feng', NULL); INSERT INTO CourseSelection (ID, StudentID, CourseID) VALUES(1, 1, 2); INSERT INTO CourseSelection (ID, StudentID, CourseID) VALUES(2, 1, 3); INSERT INTO CourseSelection (ID, StudentID, CourseID) VALUES(3, 1, 5); INSERT INTO CourseSelection (ID, StudentID, CourseID) VALUES(4, 2, 2); INSERT INTO CourseSelection (ID, StudentID, CourseID) VALUES(5, 2, 5); INSERT INTO CourseSelection (ID, StudentID, CourseID) VALUES(6, 2, 6); INSERT INTO CourseSelection (ID, StudentID, CourseID) VALUES(7, 2, 3); INSERT INTO CourseSelection (ID, StudentID, CourseID) VALUES(8, 3, 2); INSERT INTO CourseSelection (ID, StudentID, CourseID) VALUES(9, 3, 1); INSERT INTO CourseSelection (ID, StudentID, CourseID) VALUES(10, 4, 2); INSERT INTO CourseSelection (ID, StudentID, CourseID) VALUES(11, 4, 5); INSERT INTO CourseSelection (ID, StudentID, CourseID) VALUES(12, 4, 2); INSERT INTO CourseSelection (ID, StudentID, CourseID) VALUES(13, 5, 3); INSERT INTO CourseSelection (ID, StudentID, CourseID) VALUES(14, 5, 2); INSERT INTO CourseSelection (ID, StudentID, CourseID) VALUES(15, 7, 3);
下面的代码包含显示在本主题的代码示例。
#if INTERACTIVE
#r "FSharp.Data.TypeProviders.dll" #r "System.Data.dll" #r "System.Data.Linq.dll" #endif open System open Microsoft.FSharp.Data.TypeProviders open System.Data.Linq.SqlClient open System.Linq [<Generate>] type schema = SqlDataConnection<"Data Source=SERVER\INSTANCE;Initial Catalog=MyDatabase;Integrated Security=SSPI;"> let db = schema.GetDataContext() let student = db.Student let data = [1; 5; 7; 11; 18; 21] open System type Nullable<'T when 'T : ( new : unit -> 'T) and 'T : struct and 'T :> ValueType > with member this.Print() = if (this.HasValue) then this.Value.ToString() else "NULL" printfn "\ncontains query operator" query { for student in db.Student do select student.Age.Value contains 11 } |> printfn "Is at least one student age 11? %b" printfn "\ncount query operator" query { for student in db.Student do select student count } |> printfn "Number of students: %d" printfn "\nlast query operator." let num = query { for number in data do sortBy number last } printfn "Last number: %d" num open Microsoft.FSharp.Linq printfn "\nlastOrDefault query operator." query { for number in data do sortBy number lastOrDefault } |> printfn "lastOrDefault: %d" printfn "\nexactlyOne query operator." let student2 = query { for student in db.Student do where (student.StudentID = 1) select student exactlyOne } printfn "Student with StudentID = 1 is %s" student2.Name printfn "\nexactlyOneOrDefault query operator." let student3 = query { for student in db.Student do where (student.StudentID = 1) select student exactlyOneOrDefault } printfn "Student with StudentID = 1 is %s" student3.Name printfn "\nheadOrDefault query operator." let student4 = query { for student in db.Student do select student headOrDefault } printfn "head student is %s" student4.Name printfn "\nselect query operator." query { for student in db.Student do select student } |> Seq.iter (fun student -> printfn "StudentID, Name: %d %s" student.StudentID student.Name) printfn "\nwhere query operator." query { for student in db.Student do where (student.StudentID > 4) select student } |> Seq.iter (fun student -> printfn "StudentID, Name: %d %s" student.StudentID student.Name) printfn "\nminBy query operator." let student5 = query { for student in db.Student do minBy student.StudentID } printfn "\nmaxBy query operator." let student6 = query { for student in db.Student do maxBy student.StudentID } printfn "\ngroupBy query operator." query { for student in db.Student do groupBy student.Age into g select (g.Key, g.Count()) } |> Seq.iter (fun (age, count) -> printfn "Age: %s Count at that age: %d" (age.Print()) count) printfn "\nsortBy query operator." query { for student in db.Student do sortBy student.Name select student } |> Seq.iter (fun student -> printfn "StudentID, Name: %d %s" student.StudentID student.Name) printfn "\nsortByDescending query operator." query { for student in db.Student do sortByDescending student.Name select student } |> Seq.iter (fun student -> printfn "StudentID, Name: %d %s" student.StudentID student.Name) printfn "\nthenBy query operator." query { for student in db.Student do where student.Age.HasValue sortBy student.Age.Value thenBy student.Name select student } |> Seq.iter (fun student -> printfn "StudentID, Name: %d %s" student.Age.Value student.Name) printfn "\nthenByDescending query operator." query { for student in db.Student do where student.Age.HasValue sortBy student.Age.Value thenByDescending student.Name select student } |> Seq.iter (fun student -> printfn "StudentID, Name: %d %s" student.Age.Value student.Name) printfn "\ngroupValBy query operator." query { for student in db.Student do groupValBy student.Name student.Age into g select (g, g.Key, g.Count()) } |> Seq.iter (fun (group, age, count) -> printfn "Age: %s Count at that age: %d" (age.Print()) count group |> Seq.iter (fun name -> printfn "Name: %s" name)) printfn "\n sumByNullable query operator" query { for student in db.Student do sumByNullable student.Age } |> (fun sum -> printfn "Sum of ages: %s" (sum.Print())) printfn "\n minByNullable" query { for student in db.Student do minByNullable student.Age } |> (fun age -> printfn "Minimum age: %s" (age.Print())) printfn "\n maxByNullable" query { for student in db.Student do maxByNullable student.Age } |> (fun age -> printfn "Maximum age: %s" (age.Print())) printfn "\n averageBy" query { for student in db.Student do averageBy (float student.StudentID) } |> printfn "Average student ID: %f" printfn "\n averageByNullable" query { for student in db.Student do averageByNullable (Nullable.float student.Age) } |> (fun avg -> printfn "Average age: %s" (avg.Print())) printfn "\n find query operator" query { for student in db.Student do find (student.Name = "Abercrombie, Kim") } |> (fun student -> printfn "Found a match with StudentID = %d" student.StudentID) printfn "\n all query operator" query { for student in db.Student do all (SqlMethods.Like(student.Name, "%,%")) } |> printfn "Do all students have a comma in the name? %b" printfn "\n head query operator" query { for student in db.Student do head } |> (fun student -> printfn "Found the head student with StudentID = %d" student.StudentID) printfn "\n nth query operator" query { for numbers in data do nth 3 } |> printfn "Third number is %d" printfn "\n skip query operator" query { for student in db.Student do skip 1 } |> Seq.iter (fun student -> printfn "StudentID = %d" student.StudentID) printfn "\n skipWhile query operator" query { for number in data do skipWhile (number < 3) select number } |> Seq.iter (fun number -> printfn "Number = %d" number) printfn "\n sumBy query operator" query { for student in db.Student do sumBy student.StudentID } |> printfn "Sum of student IDs: %d" printfn "\n take query operator" query { for student in db.Student do select student take 2 } |> Seq.iter (fun student -> printfn "StudentID = %d" student.StudentID) printfn "\n takeWhile query operator" query { for number in data do takeWhile (number < 10) } |> Seq.iter (fun number -> printfn "Number = %d" number) printfn "\n sortByNullable query operator" query { for student in db.Student do sortByNullable student.Age select student } |> Seq.iter (fun student -> printfn "StudentID, Name, Age: %d %s %s" student.StudentID student.Name (student.Age.Print())) printfn "\n sortByNullableDescending query operator" query { for student in db.Student do sortByNullableDescending student.Age select student } |> Seq.iter (fun student -> printfn "StudentID, Name, Age: %d %s %s" student.StudentID student.Name (student.Age.Print())) printfn "\n thenByNullable query operator" query { for student in db.Student do sortBy student.Name thenByNullable student.Age select student } |> Seq.iter (fun student -> printfn "StudentID, Name, Age: %d %s %s" student.StudentID student.Name (student.Age.Print())) printfn "\n thenByNullableDescending query operator" query { for student in db.Student do sortBy student.Name thenByNullableDescending student.Age select student } |> Seq.iter (fun student -> printfn "StudentID, Name, Age: %d %s %s" student.StudentID student.Name (student.Age.Print())) printfn "All students: " query { for student in db.Student do select student } |> Seq.iter (fun student -> printfn "%s %d %s" student.Name student.StudentID (student.Age.Print())) printfn