0
D-Link不给力,没有提供这款网卡的2008 X64驱动,但我们可以使用Ralink的RT2005来代替。
为了方便大家,这里提供驱动打包下载。安装如下:
解压G122_x64_Driver.zip驱动,插入D-Link DWL-G122, 驱动目录指向解压目录即。
下载文件
| 引用(0)
CTE 通用表表达式
概念:Common Table Expression,简称CTE,中文可以叫做,通用表表达式.
用处:处理以前版本中SQL不好现实,不好理解,复杂的查询问题.比如:分页,递归查询...
基本用法:
WITH()
AS
(
)
SELECT * FROM
示例一(基本用法):
with MyCTE(ID, Name)
as
(
select EmployeeID as ID, FirstName + ' ' + LastName as Name
from HumanResources.vEmployee
)
select * from MyCTE
示例二(分页):
with MyCTE(ID, Name, RowID)
as
(
select EmployeeID as ID, FirstName + ' ' + LastName as Name,
Row_Number() over (order by EmployeeID) as RowID
from HumanResources.vEmployee
)
select * from MyCTE where RowID between 1 and 10
示例三(关联CTE):
with OrderCountCTE(SalesPersonID, OrderCount)
as
(
select SalesPersonID, count(1)
from Sales.SalesOrderHeader
where SalesPersonID is not null
group by SalesPersonID
)
select sp.SalesPersonID, sp.SalesYTD, cte.OrderCount
from OrderCountCTE cte inner join Sales.SalesPerson sp
on cte.SalesPersonID = sp.SalesPersonID order by 3
示例四(使用CTE的删除):
CREATE TABLE Products (
Product_ID int NOT NULL,
Product_Name varchar (25),
Price money NULL,
CONSTRAINT PK_Products PRIMARY KEY NONCLUSTERED (Product_ID)
)
GO
INSERT INTO Products (Product_ID, Product_Name, Price) VALUES (1, 'Widgets', 25)
INSERT INTO Products (Product_ID, Product_Name, Price) VALUES (2, 'Gadgets', 50)
INSERT INTO Products (Product_ID, Product_Name, Price) VALUES (3, 'Thingies', 75)
INSERT INTO Products (Product_ID, Product_Name, Price) VALUES (4, 'Whoozits', 90)
INSERT INTO Products (Product_ID, Product_Name, Price) VALUES (5, 'Whatzits', 5)
INSERT INTO Products (Product_ID, Product_Name, Price) VALUES (6, 'Gizmos', 15)
INSERT INTO Products (Product_ID, Product_Name, Price) VALUES (7, 'Widgets', 24)
INSERT INTO Products (Product_ID, Product_Name, Price) VALUES (8, 'Gizmos', 36)
INSERT INTO Products (Product_ID, Product_Name, Price) VALUES (9, 'Gizmos', 36)
GO
--==================Delete duplicate products=============================
with DuplicateProdCTE
as
(select Min(Product_ID) as Product_ID, Product_Name
from Products
group by Product_Name
having count(1) >1
)
delete Products from Products p join DuplicateProdCTE cte
on cte.Product_Name = p.Product_Name and p.Product_ID > cte.Product_ID
示例五(递归查询):
CREATE TABLE Employee_Tree (Employee_NM nvarchar(50), Employee_ID int PRIMARY KEY, ReportsTo int)
--insert some data, build a reporting tree
INSERT INTO Employee_Tree VALUES('Richard', 1, NULL)
INSERT INTO Employee_Tree VALUES('Stephen', 2, 1)
INSERT INTO Employee_Tree VALUES('Clemens', 3, 2)
INSERT INTO Employee_Tree VALUES('Malek', 4, 2)
INSERT INTO Employee_Tree VALUES('Goksin', 5, 4)
INSERT INTO Employee_Tree VALUES('Kimberly', 6, 1)
INSERT INTO Employee_Tree VALUES('Ramesh', 7, 5)
--
with MyCTE
as
( select Employee_ID, Employee_NM, -1 as ReportsTo, 0 as SubLevel
from Employee_Tree where ReportsTo is null --root node
union all
select e.Employee_ID, e.Employee_NM, e.ReportsTo, SubLevel +1
from Employee_Tree e, MyCTE where e.ReportsTo = MyCTE.Employee_ID
) --select * from MyCTE
select MyCTE.Employee_NM as emp , MyCTE.SubLevel, e.Employee_NM as boss
from MyCTE left join Employee_Tree e on MyCTE.ReportsTo = e.Employee_ID
--OPTION(MAXRECURSION 3) --error
--OPTION(MAXRECURSION 4) --ok
where SubLevel < 4
注意:OPTION(MAXRECURSION 4)用来设置递归时查找的层数,默认是100,如果超过默认或指定的,则会报错.通常我们用一个层数列来过滤指定的层.
--联机示例
A. 创建一个简单公用表表达式
以下示例显示直接向 Adventure Works Cycles 的每个经理报告的雇员的数目。
USE AdventureWorks2008R2;
GO
WITH Sales_CTE (SalesPersonID, NumberOfOrders)
AS
(
SELECT SalesPersonID, COUNT(*)
FROM Sales.SalesOrderHeader
WHERE SalesPersonID IS NOT NULL
GROUP BY SalesPersonID
)
SELECT SalesPersonID, NumberOfOrders
FROM Sales_CTE
ORDER BY SalesPersonID;
GO
B. 使用公用表表达式来限制次数和报告平均数
以下示例显示向经理报告的雇员的平均数。
WITH Sales_CTE (SalesPersonID, NumberOfOrders)
AS
(
SELECT SalesPersonID, COUNT(*)
FROM Sales.SalesOrderHeader
WHERE SalesPersonID IS NOT NULL
GROUP BY SalesPersonID
)
SELECT AVG(NumberOfOrders) AS [Average Sales Per Person]
FROM Sales_CTE;
GO
C. 多次引用同一个公用表表达式
以下示例显示 SalesOrderHeader 表中每个销售人员的销售订单的总数和最近的销售订单的日期。
USE AdventureWorks2008R2;
GO
-- Define the CTE expression name and column list.
WITH Sales_CTE (SalesPersonID, SalesOrderID, SalesYear)
AS
-- Define the CTE query.
(
SELECT SalesPersonID, SalesOrderID, YEAR(OrderDate) AS SalesYear
FROM Sales.SalesOrderHeader
WHERE SalesPersonID IS NOT NULL
)
-- Define the outer query referencing the CTE name.
SELECT SalesPersonID, COUNT(SalesOrderID) AS TotalSales, SalesYear
FROM Sales_CTE
GROUP BY SalesYear, SalesPersonID
ORDER BY SalesPersonID, SalesYear;
GO
使用递归公用表表达式显示递归的多个级别。
以下示例显示经理以及向经理报告的雇员的层次列表。
USE AdventureWorks2008R2;
GO
WITH DirectReports(ManagerID, EmployeeID, EmployeeLevel) AS
(
SELECT ManagerID, EmployeeID, 0 AS EmployeeLevel
FROM HumanResources.Employee
WHERE ManagerID IS NULL
UNION ALL
SELECT e.ManagerID, e.EmployeeID, EmployeeLevel + 1
FROM HumanResources.Employee e
INNER JOIN DirectReports d
ON e.ManagerID = d.EmployeeID
)
SELECT ManagerID, EmployeeID, EmployeeLevel
FROM DirectReports ;
GO
E. 使用递归公用表表达式显示递归的两个级别。
以下示例显示经理以及向经理报告的雇员。将返回的级别数目被限制为两个。
USE AdventureWorks2008R2;
GO
WITH DirectReports(ManagerID, EmployeeID, EmployeeLevel) AS
(
SELECT ManagerID, EmployeeID, 0 AS EmployeeLevel
FROM HumanResources.Employee
WHERE ManagerID IS NULL
UNION ALL
SELECT e.ManagerID, e.EmployeeID, EmployeeLevel + 1
FROM HumanResources.Employee e
INNER JOIN DirectReports d
ON e.ManagerID = d.EmployeeID
)
SELECT ManagerID, EmployeeID, EmployeeLevel
FROM DirectReports
WHERE EmployeeLevel <= 2 ;
GO
F. 使用递归公用表表达式显示层次列表
以下示例在示例 C 的基础上添加经理和雇员的名称,以及他们各自的头衔。通过缩进各个级别,突出显示经理和雇员的层次结构。
USE AdventureWorks2008R2;
GO
WITH DirectReports(Name, Title, EmployeeID, EmployeeLevel, Sort)
AS (SELECT CONVERT(varchar(255), c.FirstName + ' ' + c.LastName),
e.JobTitle,
e.EmployeeID,
1,
CONVERT(varchar(255), c.FirstName + ' ' + c.LastName)
FROM HumanResources.Employee AS e
JOIN Person.Contact AS c ON e.ContactID = c.ContactID
WHERE e.ManagerID IS NULL
UNION ALL
SELECT CONVERT(varchar(255), REPLICATE ('| ' , EmployeeLevel) +
c.FirstName + ' ' + c.LastName),
e.JobTitle,
e.EmployeeID,
EmployeeLevel + 1,
CONVERT (varchar(255), RTRIM(Sort) + '| ' + FirstName + ' ' +
LastName)
FROM HumanResources.Employee as e
JOIN Person.Contact AS c ON e.ContactID = c.ContactID
JOIN DirectReports AS d ON e.ManagerID = d.EmployeeID
)
SELECT EmployeeID, Name, Title, EmployeeLevel
FROM DirectReports
ORDER BY Sort;
GO
G. 使用 MAXRECURSION 取消一条语句
可以使用 MAXRECURSION 来防止不合理的递归 CTE 进入无限循环。下面的示例特意创建了一个无限循环,然后使用 MAXRECURSION 提示将递归级别限制为两级。
USE AdventureWorks2008R2;
GO
--Creates an infinite loop.
WITH cte (CustomerID, PersonID, StoreID) AS
(
SELECT CustomerID, PersonID, StoreID
FROM Sales.Customer
WHERE PersonID IS NOT NULL
UNION ALL
SELECT cte.CustomerID, cte.PersonID, cte.StoreID
FROM cte
JOIN Sales.Customer AS c
ON cte.PersonID = c.CustomerID
)
--Uses MAXRECURSION to limit the recursive levels to 2.
SELECT CustomerID, PersonID, StoreID
FROM cte
OPTION (MAXRECURSION 2);
GO
在更正代码错误之后,就不再需要 MAXRECURSION。以下示例显示了更正后的代码。
USE AdventureWorks2008R2;
GO
WITH cte (EmployeeID, ManagerID, Title)
AS
(
SELECT EmployeeID, ManagerID, JobTitle
FROM HumanResources.Employee
WHERE ManagerID IS NOT NULL
UNION ALL
SELECT e.EmployeeID, e.ManagerID, e.JobTitle
FROM HumanResources.Employee AS e
JOIN cte ON e.ManagerID = cte.EmployeeID
)
SELECT EmployeeID, ManagerID, Title
FROM cte;
GO
H. 使用公用表表达式来有选择地执行 SELECT 语句中的递归操作
以下示例显示了为 ProductAssemblyID = 800 生产自行车所需的产品装配和部件层次结构。
USE AdventureWorks2008R2;
GO
WITH Parts(AssemblyID, ComponentID, PerAssemblyQty, EndDate, ComponentLevel) AS
(
SELECT b.ProductAssemblyID, b.ComponentID, b.PerAssemblyQty,
b.EndDate, 0 AS ComponentLevel
FROM Production.BillOfMaterials AS b
WHERE b.ProductAssemblyID = 800
AND b.EndDate IS NULL
UNION ALL
SELECT bom.ProductAssemblyID, bom.ComponentID, p.PerAssemblyQty,
bom.EndDate, ComponentLevel + 1
FROM Production.BillOfMaterials AS bom
INNER JOIN Parts AS p
ON bom.ProductAssemblyID = p.ComponentID
AND bom.EndDate IS NULL
)
SELECT AssemblyID, ComponentID, Name, PerAssemblyQty, EndDate,
ComponentLevel
FROM Parts AS p
INNER JOIN Production.Product AS pr
ON p.ComponentID = pr.ProductID
ORDER BY ComponentLevel, AssemblyID, ComponentID;
GO
I. 在 UPDATE 语句中使用递归 CTE
以下示例将直接或间接向 ManagerID 12 报告的所有雇员的 VacationHours 值增加 25%。公用表表达式将返回直接向 ManagerID 12 报告的雇员以及直接向这些雇员报告的雇员等的层次列表。只修改公用表表达式所返回的行。
USE AdventureWorks2008R2;
GO
WITH Parts(AssemblyID, ComponentID, PerAssemblyQty, EndDate, ComponentLevel) AS
(
SELECT b.ProductAssemblyID, b.ComponentID, b.PerAssemblyQty,
b.EndDate, 0 AS ComponentLevel
FROM Production.BillOfMaterials AS b
WHERE b.ProductAssemblyID = 800
AND b.EndDate IS NULL
UNION ALL
SELECT bom.ProductAssemblyID, bom.ComponentID, p.PerAssemblyQty,
bom.EndDate, ComponentLevel + 1
FROM Production.BillOfMaterials AS bom
INNER JOIN Parts AS p
ON bom.ProductAssemblyID = p.ComponentID
AND bom.EndDate IS NULL
)
UPDATE Production.BillOfMaterials
SET PerAssemblyQty = c.PerAssemblyQty * 2
FROM Production.BillOfMaterials AS c
JOIN Parts AS d ON c.ProductAssemblyID = d.AssemblyID
WHERE d.ComponentLevel = 0;
使用多个定位点和递归成员
以下示例使用多个定位点和递归成员来返回指定的人的所有祖先。创建了一个表,并在表中插入值,以建立由递归 CTE 返回的宗谱。
-- Genealogy table
IF OBJECT_ID('dbo.Person','U') IS NOT NULL DROP TABLE dbo.Person;
GO
CREATE TABLE dbo.Person(ID int, Name varchar(30), Mother int, Father int);
GO
INSERT dbo.Person
VALUES(1, 'Sue', NULL, NULL)
,(2, 'Ed', NULL, NULL)
,(3, 'Emma', 1, 2)
,(4, 'Jack', 1, 2)
,(5, 'Jane', NULL, NULL)
,(6, 'Bonnie', 5, 4)
,(7, 'Bill', 5, 4);
GO
-- Create the recursive CTE to find all of Bonnie's ancestors.
WITH Generation (ID) AS
(
-- First anchor member returns Bonnie's mother.
SELECT Mother
FROM dbo.Person
WHERE Name = 'Bonnie'
UNION
-- Second anchor member returns Bonnie's father.
SELECT Father
FROM dbo.Person
WHERE Name = 'Bonnie'
UNION ALL
-- First recursive member returns male ancestors of the previous generation.
SELECT Person.Father
FROM Generation, Person
WHERE Generation.ID=Person.ID
UNION ALL
-- Second recursive member returns female ancestors of the previous generation.
SELECT Person.Mother
FROM Generation, dbo.Person
WHERE Generation.ID=Person.ID
)
SELECT Person.ID, Person.Name, Person.Mother, Person.Father
FROM Generation, dbo.Person
WHERE Generation.ID = Person.ID;
GO
--利用cte实现递归
--> 测试数据:[tb]
if object_id('[tb]') is not null drop table [tb]
create table [tb]([id] int,[col1] varchar(8),[col2] int)
insert [tb]
select 1,'河北省',0 union all
select 2,'邢台市',1 union all
select 3,'石家庄市',1 union all
select 4,'张家口市',1 union all
select 5,'南宫',2 union all
select 6,'坝上',4 union all
select 7,'任县',2 union all
select 8,'清河',2 union all
select 9,'河南省',0 union all
select 10,'新乡市',9 union all
select 11,'aaa',10 union all
select 12,'bbb',10
;with t as(
select * from [tb] where col1='河北省'
union all
select a.* from [tb] a ,t where a.col2=t.id
)
select * from t
/*
id col1 col2
----------- -------- -----------
1 河北省 0
2 邢台市 1
3 石家庄市 1
4 张家口市 1
6 坝上 4
5 南宫 2
7 任县 2
8 清河 2
(8 行受影响)
*/
下面的准则适用于定义递归公用表表达式:
递归 CTE 定义至少必须包含两个 CTE 查询定义,一个定位点成员和一个递归成员。可以定义多个定位点成员和递归成员;但必须将所有定位点成员查询定义置于第一个递归成员定义之前。所有 CTE 查询定义都是定位点成员,但它们引用 CTE 本身时除外。
Anchor members must be combined by one of these set operators: UNION ALL, UNION, INTERSECT, or EXCEPT.在最后一个定位点成员和第一个递归成员之间,以及组合多个递归成员时,只能使用 UNION ALL 集合运算符。
定位点成员和递归成员中的列数必须一致。
递归成员中列的数据类型必须与定位点成员中相应列的数据类型一致。
递归成员的 FROM 子句只能引用一次 CTE expression_name。
在递归成员的 CTE_query_definition 中不允许出现下列项:
SELECT DISTINCT
GROUP BY
HAVING
标量聚合
TOP
LEFT、RIGHT、OUTER JOIN(允许出现 INNER JOIN)
子查询
应用于对 CTE_query_definition 中的 CTE 的递归引用的提示。
下面的准则适用于使用递归公用表表达式:
无论参与的 SELECT 语句返回的列的为 Null 性如何,递归 CTE 返回的全部列都可以为空。
如果递归 CTE 组合不正确,可能会导致无限循环。例如,如果递归成员查询定义对父列和子列返回相同的值,则会造成无限循环。可以使用 MAXRECURSION 提示以及在 INSERT、UPDATE、MERGE、DELETE 或 SELECT 语句的 OPTION 子句中的一个 0 到 32,767 之间的值,来限制特定语句所允许的递归级数,以防止出现无限循环。这样就能够在解决产生循环的代码问题之前控制语句的执行。服务器范围内的默认值是 100。如果指定 0,则没有限制。每一个语句只能指定一个 MAXRECURSION 值。有关详细信息,请参阅查询提示 (Transact-SQL)。
不能使用包含递归公用表表达式的视图来更新数据。
可以使用 CTE 在查询上定义游标。CTE 是定义游标结果集的 select_statement 参数。递归 CTE 只允许使用快速只进游标和静态(快照)游标。如果在递归 CTE 中指定了其他游标类型,则该类型将转换为静态游标类型。
可以在 CTE 中引用远程服务器中的表。如果在 CTE 的递归成员中引用了远程服务器,那么将为每个远程表创建一个假脱机,这样就可以在本地反复访问这些表。如果为 CTE 查询,Index Spool/Lazy Spool 则显示在查询计划中,并具有额外的 WITH STACK 谓词。这是一种确认正确递归的方法。
SQL Server 2008 不允许在 CTE 的递归部分中使用分析和聚合函数。
--生成一个数字表,效率非常高
create function dbo.fn_nums(@n as bigint)
returns table
as
return
with
t1 as (select 1 as c union all select 1),
t2 as (select 1 as c from t1 as a,t1 as b),
t3 as (select 1 as c from t2 as a,t2 as b),
t4 as (select 1 as c from t3 as a,t3 as b),
t5 as (select 1 as c from t4 as a,t4 as b),
t6 as (select 1 as c from t5 as a,t5 as b),
t7 as (select row_number() over(order by c) as n from t6)
select n from t7 where n<@n;
go
--测试
select * from dbo.fn_nums(1000)
--更新或者删除前n跳数据
;with t as
(
select top n from tb order by id desc
)
update t set .....
--
or
delete t
--3.如果CTE的表达式名称与某个数据表或视图重名,则紧跟在该CTE后面的SQL语句使用的仍然是CTE,当然,后面的SQL语句使用的就是数据表或视图了,如下面的SQL语句所示:
-- table1是一个实际存在的表
with table1 as
(
select * from persons where age<30
)
select * from table1 -- 使用了名为table1的公共表表达式
select * from table1 -- 使用了名为table1的数据表
--4.CTE 可以引用自身,也可以引用在同一 WITH 子句中预先定义的 CTE。不允许前向引用。
--5.不能在 CTE_query_definition 中使用以下子句:
(1)COMPUTE 或 COMPUTE BY
(2)ORDER BY(除非指定了 TOP 子句)
(3)INTO
(4)带有查询提示的 OPTION 子句
(5)FOR XML
(6)FOR BROWSE
--6.如果将 CTE 用在属于批处理的一部分的语句中,那么在它之前的语句必须以分号结尾,如下面的SQL所示:
declare @s nvarchar(3)
set @s = 'C%'
; --必须加分号
with t_tree as
(
select CountryRegionCode from person.CountryRegion where Name like@s
)
select * from person.StateProvince where CountryRegionCodein(select * fromt_tree)
create table #EnterPrise
(
Department nvarchar(50),--部门名称
ParentDept nvarchar(50),--上级部门
DepartManage nvarchar(30)--部门经理
)
insert into #EnterPrise select '技术部','总经办','Tom'
insert into #EnterPrise select '商务部','总经办','Jeffry'
insert into #EnterPrise select '商务一部','商务部','ViVi'
insert into #EnterPrise select '商务二部','商务部','Peter'
insert into #EnterPrise select '程序组','技术部','GiGi'
insert into #EnterPrise select '设计组','技术部','yoyo'
insert into #EnterPrise select '专项组','程序组','Yue'
insert into #EnterPrise select '总经办','','Boss'
--查询部门经理是Tom的下面的部门名称
;with hgo as
(
select *,0 as rank from #EnterPrise where DepartManage='Tom'
union all
select h.*,h1.rank+1 from #EnterPrise h join hgo h1 on h.ParentDept=h1.Department
)
select * from hgo
/*
Department ParentDept DepartManage rank
--------------- -------------------- ----------------------- -----------
技术部 总经办 Tom 0
程序组 技术部 GiGi 1
设计组 技术部 yoyo 1
专项组 程序组 Yue 2
*/
--查询部门经理是GiGi的上级部门名称
;with hgo as
(
select *,0 as rank from #EnterPrise where DepartManage='GiGi'
union all
select h.*,h1.rank+1 from #EnterPrise h join hgo h1 on h.Department=h1.ParentDept
)
select * from hgo
/*
Department ParentDept DepartManage rank
-------------------- ---------------------- ----------- -----------
程序组 技术部 GiGi 0
技术部 总经办 Tom 1
总经办 Boss 2
*/
USE tempdb
GO
-- 建立演示环境
CREATE TABLE Dept(
id int PRIMARY KEY,
parent_id int,
name nvarchar(20))
INSERT Dept
SELECT 0, 0, N'<全部>' UNION ALL
SELECT 1, 0, N'财务部' UNION ALL
SELECT 2, 0, N'行政部' UNION ALL
SELECT 3, 0, N'业务部' UNION ALL
SELECT 4, 0, N'业务部' UNION ALL
SELECT 5, 4, N'销售部' UNION ALL
SELECT 6, 4, N'MIS' UNION ALL
SELECT 7, 6, N'UI' UNION ALL
SELECT 8, 6, N'软件开发' UNION ALL
SELECT 9, 8, N'内部开发'
GO
-- 查询指定部门下面的所有部门
DECLARE @Dept_name nvarchar(20)
SET @Dept_name = N'MIS'
;WITH
DEPTS AS(
-- 定位点成员
SELECT * FROM Dept
WHERE name = @Dept_name
UNION ALL
-- 递归成员, 通过引用CTE自身与Dept基表JOIN实现递归
SELECT A.*
FROM Dept A, DEPTS B
WHERE A.parent_id = B.id
)
SELECT * FROM DEPTS
GO
-- 删除演示环境
DROP TABLE Dept
----CTE的综合应用
USE tempdb
GO
-- 建立演示环境
CREATE TABLE Dept(
id int PRIMARY KEY,
parent_id int,
name nvarchar(20))
INSERT Dept
SELECT 0, 0, N'<全部>' UNION ALL
SELECT 1, 0, N'财务部' UNION ALL
SELECT 2, 0, N'行政部' UNION ALL
SELECT 3, 0, N'业务部' UNION ALL
SELECT 4, 0, N'业务部' UNION ALL
SELECT 5, 4, N'销售部' UNION ALL
SELECT 6, 4, N'MIS' UNION ALL
SELECT 7, 6, N'UI' UNION ALL
SELECT 8, 6, N'软件开发' UNION ALL
SELECT 9, 8, N'内部开发'
GO
-- 查询指定部门下面的所有部门, 并汇总各部门的下级部门数
DECLARE @Dept_name nvarchar(20)
SET @Dept_name = N'MIS'
;WITH
DEPTS AS( -- 查询指定部门及其下的所有子部门
-- 定位点成员
SELECT * FROM Dept
WHERE name = @Dept_name
UNION ALL
-- 递归成员, 通过引用CTE自身与Dept基表JOIN实现递归
SELECT A.*
FROM Dept A, DEPTS B
WHERE A.parent_id = B.id
),
DEPTCHILD AS( -- 引用第1个CTE,查询其每条记录对应的部门下的所有子部门
SELECT
Dept_id = P.id, C.id, C.parent_id
FROM DEPTS P, Dept C
WHERE P.id = C.parent_id
UNION ALL
SELECT
P.Dept_id, C.id, C.parent_id
FROM DEPTCHILD P, Dept C
WHERE P.id = C.parent_id
),
DEPTCHILDCNT AS( -- 引用第2个CTE, 汇总得到各部门下的子部门数
SELECT
Dept_id, Cnt = COUNT(*)
FROM DEPTCHILD
GROUP BY Dept_id
)
SELECT -- JOIN第1,3个CTE,得到最终的查询结果
D.*,
ChildDeptCount = ISNULL(DS.Cnt, 0)
FROM DEPTS D
LEFT JOIN DEPTCHILDCNT DS
ON D.id = DS.Dept_id
GO
-- 删除演示环境
DROP TABLE Dept
概念:Common Table Expression,简称CTE,中文可以叫做,通用表表达式.
用处:处理以前版本中SQL不好现实,不好理解,复杂的查询问题.比如:分页,递归查询...
基本用法:
WITH
AS
(
)
SELECT * FROM
示例一(基本用法):
with MyCTE(ID, Name)
as
(
select EmployeeID as ID, FirstName + ' ' + LastName as Name
from HumanResources.vEmployee
)
select * from MyCTE
示例二(分页):
with MyCTE(ID, Name, RowID)
as
(
select EmployeeID as ID, FirstName + ' ' + LastName as Name,
Row_Number() over (order by EmployeeID) as RowID
from HumanResources.vEmployee
)
select * from MyCTE where RowID between 1 and 10
示例三(关联CTE):
with OrderCountCTE(SalesPersonID, OrderCount)
as
(
select SalesPersonID, count(1)
from Sales.SalesOrderHeader
where SalesPersonID is not null
group by SalesPersonID
)
select sp.SalesPersonID, sp.SalesYTD, cte.OrderCount
from OrderCountCTE cte inner join Sales.SalesPerson sp
on cte.SalesPersonID = sp.SalesPersonID order by 3
示例四(使用CTE的删除):
CREATE TABLE Products (
Product_ID int NOT NULL,
Product_Name varchar (25),
Price money NULL,
CONSTRAINT PK_Products PRIMARY KEY NONCLUSTERED (Product_ID)
)
GO
INSERT INTO Products (Product_ID, Product_Name, Price) VALUES (1, 'Widgets', 25)
INSERT INTO Products (Product_ID, Product_Name, Price) VALUES (2, 'Gadgets', 50)
INSERT INTO Products (Product_ID, Product_Name, Price) VALUES (3, 'Thingies', 75)
INSERT INTO Products (Product_ID, Product_Name, Price) VALUES (4, 'Whoozits', 90)
INSERT INTO Products (Product_ID, Product_Name, Price) VALUES (5, 'Whatzits', 5)
INSERT INTO Products (Product_ID, Product_Name, Price) VALUES (6, 'Gizmos', 15)
INSERT INTO Products (Product_ID, Product_Name, Price) VALUES (7, 'Widgets', 24)
INSERT INTO Products (Product_ID, Product_Name, Price) VALUES (8, 'Gizmos', 36)
INSERT INTO Products (Product_ID, Product_Name, Price) VALUES (9, 'Gizmos', 36)
GO
--==================Delete duplicate products=============================
with DuplicateProdCTE
as
(select Min(Product_ID) as Product_ID, Product_Name
from Products
group by Product_Name
having count(1) >1
)
delete Products from Products p join DuplicateProdCTE cte
on cte.Product_Name = p.Product_Name and p.Product_ID > cte.Product_ID
示例五(递归查询):
CREATE TABLE Employee_Tree (Employee_NM nvarchar(50), Employee_ID int PRIMARY KEY, ReportsTo int)
--insert some data, build a reporting tree
INSERT INTO Employee_Tree VALUES('Richard', 1, NULL)
INSERT INTO Employee_Tree VALUES('Stephen', 2, 1)
INSERT INTO Employee_Tree VALUES('Clemens', 3, 2)
INSERT INTO Employee_Tree VALUES('Malek', 4, 2)
INSERT INTO Employee_Tree VALUES('Goksin', 5, 4)
INSERT INTO Employee_Tree VALUES('Kimberly', 6, 1)
INSERT INTO Employee_Tree VALUES('Ramesh', 7, 5)
--
with MyCTE
as
( select Employee_ID, Employee_NM, -1 as ReportsTo, 0 as SubLevel
from Employee_Tree where ReportsTo is null --root node
union all
select e.Employee_ID, e.Employee_NM, e.ReportsTo, SubLevel +1
from Employee_Tree e, MyCTE where e.ReportsTo = MyCTE.Employee_ID
) --select * from MyCTE
select MyCTE.Employee_NM as emp , MyCTE.SubLevel, e.Employee_NM as boss
from MyCTE left join Employee_Tree e on MyCTE.ReportsTo = e.Employee_ID
--OPTION(MAXRECURSION 3) --error
--OPTION(MAXRECURSION 4) --ok
where SubLevel < 4
注意:OPTION(MAXRECURSION 4)用来设置递归时查找的层数,默认是100,如果超过默认或指定的,则会报错.通常我们用一个层数列来过滤指定的层.
--联机示例
A. 创建一个简单公用表表达式
以下示例显示直接向 Adventure Works Cycles 的每个经理报告的雇员的数目。
USE AdventureWorks2008R2;
GO
WITH Sales_CTE (SalesPersonID, NumberOfOrders)
AS
(
SELECT SalesPersonID, COUNT(*)
FROM Sales.SalesOrderHeader
WHERE SalesPersonID IS NOT NULL
GROUP BY SalesPersonID
)
SELECT SalesPersonID, NumberOfOrders
FROM Sales_CTE
ORDER BY SalesPersonID;
GO
B. 使用公用表表达式来限制次数和报告平均数
以下示例显示向经理报告的雇员的平均数。
WITH Sales_CTE (SalesPersonID, NumberOfOrders)
AS
(
SELECT SalesPersonID, COUNT(*)
FROM Sales.SalesOrderHeader
WHERE SalesPersonID IS NOT NULL
GROUP BY SalesPersonID
)
SELECT AVG(NumberOfOrders) AS [Average Sales Per Person]
FROM Sales_CTE;
GO
C. 多次引用同一个公用表表达式
以下示例显示 SalesOrderHeader 表中每个销售人员的销售订单的总数和最近的销售订单的日期。
USE AdventureWorks2008R2;
GO
-- Define the CTE expression name and column list.
WITH Sales_CTE (SalesPersonID, SalesOrderID, SalesYear)
AS
-- Define the CTE query.
(
SELECT SalesPersonID, SalesOrderID, YEAR(OrderDate) AS SalesYear
FROM Sales.SalesOrderHeader
WHERE SalesPersonID IS NOT NULL
)
-- Define the outer query referencing the CTE name.
SELECT SalesPersonID, COUNT(SalesOrderID) AS TotalSales, SalesYear
FROM Sales_CTE
GROUP BY SalesYear, SalesPersonID
ORDER BY SalesPersonID, SalesYear;
GO
使用递归公用表表达式显示递归的多个级别。
以下示例显示经理以及向经理报告的雇员的层次列表。
USE AdventureWorks2008R2;
GO
WITH DirectReports(ManagerID, EmployeeID, EmployeeLevel) AS
(
SELECT ManagerID, EmployeeID, 0 AS EmployeeLevel
FROM HumanResources.Employee
WHERE ManagerID IS NULL
UNION ALL
SELECT e.ManagerID, e.EmployeeID, EmployeeLevel + 1
FROM HumanResources.Employee e
INNER JOIN DirectReports d
ON e.ManagerID = d.EmployeeID
)
SELECT ManagerID, EmployeeID, EmployeeLevel
FROM DirectReports ;
GO
E. 使用递归公用表表达式显示递归的两个级别。
以下示例显示经理以及向经理报告的雇员。将返回的级别数目被限制为两个。
USE AdventureWorks2008R2;
GO
WITH DirectReports(ManagerID, EmployeeID, EmployeeLevel) AS
(
SELECT ManagerID, EmployeeID, 0 AS EmployeeLevel
FROM HumanResources.Employee
WHERE ManagerID IS NULL
UNION ALL
SELECT e.ManagerID, e.EmployeeID, EmployeeLevel + 1
FROM HumanResources.Employee e
INNER JOIN DirectReports d
ON e.ManagerID = d.EmployeeID
)
SELECT ManagerID, EmployeeID, EmployeeLevel
FROM DirectReports
WHERE EmployeeLevel <= 2 ;
GO
F. 使用递归公用表表达式显示层次列表
以下示例在示例 C 的基础上添加经理和雇员的名称,以及他们各自的头衔。通过缩进各个级别,突出显示经理和雇员的层次结构。
USE AdventureWorks2008R2;
GO
WITH DirectReports(Name, Title, EmployeeID, EmployeeLevel, Sort)
AS (SELECT CONVERT(varchar(255), c.FirstName + ' ' + c.LastName),
e.JobTitle,
e.EmployeeID,
1,
CONVERT(varchar(255), c.FirstName + ' ' + c.LastName)
FROM HumanResources.Employee AS e
JOIN Person.Contact AS c ON e.ContactID = c.ContactID
WHERE e.ManagerID IS NULL
UNION ALL
SELECT CONVERT(varchar(255), REPLICATE ('| ' , EmployeeLevel) +
c.FirstName + ' ' + c.LastName),
e.JobTitle,
e.EmployeeID,
EmployeeLevel + 1,
CONVERT (varchar(255), RTRIM(Sort) + '| ' + FirstName + ' ' +
LastName)
FROM HumanResources.Employee as e
JOIN Person.Contact AS c ON e.ContactID = c.ContactID
JOIN DirectReports AS d ON e.ManagerID = d.EmployeeID
)
SELECT EmployeeID, Name, Title, EmployeeLevel
FROM DirectReports
ORDER BY Sort;
GO
G. 使用 MAXRECURSION 取消一条语句
可以使用 MAXRECURSION 来防止不合理的递归 CTE 进入无限循环。下面的示例特意创建了一个无限循环,然后使用 MAXRECURSION 提示将递归级别限制为两级。
USE AdventureWorks2008R2;
GO
--Creates an infinite loop.
WITH cte (CustomerID, PersonID, StoreID) AS
(
SELECT CustomerID, PersonID, StoreID
FROM Sales.Customer
WHERE PersonID IS NOT NULL
UNION ALL
SELECT cte.CustomerID, cte.PersonID, cte.StoreID
FROM cte
JOIN Sales.Customer AS c
ON cte.PersonID = c.CustomerID
)
--Uses MAXRECURSION to limit the recursive levels to 2.
SELECT CustomerID, PersonID, StoreID
FROM cte
OPTION (MAXRECURSION 2);
GO
在更正代码错误之后,就不再需要 MAXRECURSION。以下示例显示了更正后的代码。
USE AdventureWorks2008R2;
GO
WITH cte (EmployeeID, ManagerID, Title)
AS
(
SELECT EmployeeID, ManagerID, JobTitle
FROM HumanResources.Employee
WHERE ManagerID IS NOT NULL
UNION ALL
SELECT e.EmployeeID, e.ManagerID, e.JobTitle
FROM HumanResources.Employee AS e
JOIN cte ON e.ManagerID = cte.EmployeeID
)
SELECT EmployeeID, ManagerID, Title
FROM cte;
GO
H. 使用公用表表达式来有选择地执行 SELECT 语句中的递归操作
以下示例显示了为 ProductAssemblyID = 800 生产自行车所需的产品装配和部件层次结构。
USE AdventureWorks2008R2;
GO
WITH Parts(AssemblyID, ComponentID, PerAssemblyQty, EndDate, ComponentLevel) AS
(
SELECT b.ProductAssemblyID, b.ComponentID, b.PerAssemblyQty,
b.EndDate, 0 AS ComponentLevel
FROM Production.BillOfMaterials AS b
WHERE b.ProductAssemblyID = 800
AND b.EndDate IS NULL
UNION ALL
SELECT bom.ProductAssemblyID, bom.ComponentID, p.PerAssemblyQty,
bom.EndDate, ComponentLevel + 1
FROM Production.BillOfMaterials AS bom
INNER JOIN Parts AS p
ON bom.ProductAssemblyID = p.ComponentID
AND bom.EndDate IS NULL
)
SELECT AssemblyID, ComponentID, Name, PerAssemblyQty, EndDate,
ComponentLevel
FROM Parts AS p
INNER JOIN Production.Product AS pr
ON p.ComponentID = pr.ProductID
ORDER BY ComponentLevel, AssemblyID, ComponentID;
GO
I. 在 UPDATE 语句中使用递归 CTE
以下示例将直接或间接向 ManagerID 12 报告的所有雇员的 VacationHours 值增加 25%。公用表表达式将返回直接向 ManagerID 12 报告的雇员以及直接向这些雇员报告的雇员等的层次列表。只修改公用表表达式所返回的行。
USE AdventureWorks2008R2;
GO
WITH Parts(AssemblyID, ComponentID, PerAssemblyQty, EndDate, ComponentLevel) AS
(
SELECT b.ProductAssemblyID, b.ComponentID, b.PerAssemblyQty,
b.EndDate, 0 AS ComponentLevel
FROM Production.BillOfMaterials AS b
WHERE b.ProductAssemblyID = 800
AND b.EndDate IS NULL
UNION ALL
SELECT bom.ProductAssemblyID, bom.ComponentID, p.PerAssemblyQty,
bom.EndDate, ComponentLevel + 1
FROM Production.BillOfMaterials AS bom
INNER JOIN Parts AS p
ON bom.ProductAssemblyID = p.ComponentID
AND bom.EndDate IS NULL
)
UPDATE Production.BillOfMaterials
SET PerAssemblyQty = c.PerAssemblyQty * 2
FROM Production.BillOfMaterials AS c
JOIN Parts AS d ON c.ProductAssemblyID = d.AssemblyID
WHERE d.ComponentLevel = 0;
使用多个定位点和递归成员
以下示例使用多个定位点和递归成员来返回指定的人的所有祖先。创建了一个表,并在表中插入值,以建立由递归 CTE 返回的宗谱。
-- Genealogy table
IF OBJECT_ID('dbo.Person','U') IS NOT NULL DROP TABLE dbo.Person;
GO
CREATE TABLE dbo.Person(ID int, Name varchar(30), Mother int, Father int);
GO
INSERT dbo.Person
VALUES(1, 'Sue', NULL, NULL)
,(2, 'Ed', NULL, NULL)
,(3, 'Emma', 1, 2)
,(4, 'Jack', 1, 2)
,(5, 'Jane', NULL, NULL)
,(6, 'Bonnie', 5, 4)
,(7, 'Bill', 5, 4);
GO
-- Create the recursive CTE to find all of Bonnie's ancestors.
WITH Generation (ID) AS
(
-- First anchor member returns Bonnie's mother.
SELECT Mother
FROM dbo.Person
WHERE Name = 'Bonnie'
UNION
-- Second anchor member returns Bonnie's father.
SELECT Father
FROM dbo.Person
WHERE Name = 'Bonnie'
UNION ALL
-- First recursive member returns male ancestors of the previous generation.
SELECT Person.Father
FROM Generation, Person
WHERE Generation.ID=Person.ID
UNION ALL
-- Second recursive member returns female ancestors of the previous generation.
SELECT Person.Mother
FROM Generation, dbo.Person
WHERE Generation.ID=Person.ID
)
SELECT Person.ID, Person.Name, Person.Mother, Person.Father
FROM Generation, dbo.Person
WHERE Generation.ID = Person.ID;
GO
--利用cte实现递归
--> 测试数据:[tb]
if object_id('[tb]') is not null drop table [tb]
create table [tb]([id] int,[col1] varchar(8),[col2] int)
insert [tb]
select 1,'河北省',0 union all
select 2,'邢台市',1 union all
select 3,'石家庄市',1 union all
select 4,'张家口市',1 union all
select 5,'南宫',2 union all
select 6,'坝上',4 union all
select 7,'任县',2 union all
select 8,'清河',2 union all
select 9,'河南省',0 union all
select 10,'新乡市',9 union all
select 11,'aaa',10 union all
select 12,'bbb',10
;with t as(
select * from [tb] where col1='河北省'
union all
select a.* from [tb] a ,t where a.col2=t.id
)
select * from t
/*
id col1 col2
----------- -------- -----------
1 河北省 0
2 邢台市 1
3 石家庄市 1
4 张家口市 1
6 坝上 4
5 南宫 2
7 任县 2
8 清河 2
(8 行受影响)
*/
下面的准则适用于定义递归公用表表达式:
递归 CTE 定义至少必须包含两个 CTE 查询定义,一个定位点成员和一个递归成员。可以定义多个定位点成员和递归成员;但必须将所有定位点成员查询定义置于第一个递归成员定义之前。所有 CTE 查询定义都是定位点成员,但它们引用 CTE 本身时除外。
Anchor members must be combined by one of these set operators: UNION ALL, UNION, INTERSECT, or EXCEPT.在最后一个定位点成员和第一个递归成员之间,以及组合多个递归成员时,只能使用 UNION ALL 集合运算符。
定位点成员和递归成员中的列数必须一致。
递归成员中列的数据类型必须与定位点成员中相应列的数据类型一致。
递归成员的 FROM 子句只能引用一次 CTE expression_name。
在递归成员的 CTE_query_definition 中不允许出现下列项:
SELECT DISTINCT
GROUP BY
HAVING
标量聚合
TOP
LEFT、RIGHT、OUTER JOIN(允许出现 INNER JOIN)
子查询
应用于对 CTE_query_definition 中的 CTE 的递归引用的提示。
下面的准则适用于使用递归公用表表达式:
无论参与的 SELECT 语句返回的列的为 Null 性如何,递归 CTE 返回的全部列都可以为空。
如果递归 CTE 组合不正确,可能会导致无限循环。例如,如果递归成员查询定义对父列和子列返回相同的值,则会造成无限循环。可以使用 MAXRECURSION 提示以及在 INSERT、UPDATE、MERGE、DELETE 或 SELECT 语句的 OPTION 子句中的一个 0 到 32,767 之间的值,来限制特定语句所允许的递归级数,以防止出现无限循环。这样就能够在解决产生循环的代码问题之前控制语句的执行。服务器范围内的默认值是 100。如果指定 0,则没有限制。每一个语句只能指定一个 MAXRECURSION 值。有关详细信息,请参阅查询提示 (Transact-SQL)。
不能使用包含递归公用表表达式的视图来更新数据。
可以使用 CTE 在查询上定义游标。CTE 是定义游标结果集的 select_statement 参数。递归 CTE 只允许使用快速只进游标和静态(快照)游标。如果在递归 CTE 中指定了其他游标类型,则该类型将转换为静态游标类型。
可以在 CTE 中引用远程服务器中的表。如果在 CTE 的递归成员中引用了远程服务器,那么将为每个远程表创建一个假脱机,这样就可以在本地反复访问这些表。如果为 CTE 查询,Index Spool/Lazy Spool 则显示在查询计划中,并具有额外的 WITH STACK 谓词。这是一种确认正确递归的方法。
SQL Server 2008 不允许在 CTE 的递归部分中使用分析和聚合函数。
--生成一个数字表,效率非常高
create function dbo.fn_nums(@n as bigint)
returns table
as
return
with
t1 as (select 1 as c union all select 1),
t2 as (select 1 as c from t1 as a,t1 as b),
t3 as (select 1 as c from t2 as a,t2 as b),
t4 as (select 1 as c from t3 as a,t3 as b),
t5 as (select 1 as c from t4 as a,t4 as b),
t6 as (select 1 as c from t5 as a,t5 as b),
t7 as (select row_number() over(order by c) as n from t6)
select n from t7 where n<@n;
go
--测试
select * from dbo.fn_nums(1000)
--更新或者删除前n跳数据
;with t as
(
select top n from tb order by id desc
)
update t set .....
--
or
delete t
--3.如果CTE的表达式名称与某个数据表或视图重名,则紧跟在该CTE后面的SQL语句使用的仍然是CTE,当然,后面的SQL语句使用的就是数据表或视图了,如下面的SQL语句所示:
-- table1是一个实际存在的表
with table1 as
(
select * from persons where age<30
)
select * from table1 -- 使用了名为table1的公共表表达式
select * from table1 -- 使用了名为table1的数据表
--4.CTE 可以引用自身,也可以引用在同一 WITH 子句中预先定义的 CTE。不允许前向引用。
--5.不能在 CTE_query_definition 中使用以下子句:
(1)COMPUTE 或 COMPUTE BY
(2)ORDER BY(除非指定了 TOP 子句)
(3)INTO
(4)带有查询提示的 OPTION 子句
(5)FOR XML
(6)FOR BROWSE
--6.如果将 CTE 用在属于批处理的一部分的语句中,那么在它之前的语句必须以分号结尾,如下面的SQL所示:
declare @s nvarchar(3)
set @s = 'C%'
; --必须加分号
with t_tree as
(
select CountryRegionCode from person.CountryRegion where Name like@s
)
select * from person.StateProvince where CountryRegionCodein(select * fromt_tree)
create table #EnterPrise
(
Department nvarchar(50),--部门名称
ParentDept nvarchar(50),--上级部门
DepartManage nvarchar(30)--部门经理
)
insert into #EnterPrise select '技术部','总经办','Tom'
insert into #EnterPrise select '商务部','总经办','Jeffry'
insert into #EnterPrise select '商务一部','商务部','ViVi'
insert into #EnterPrise select '商务二部','商务部','Peter'
insert into #EnterPrise select '程序组','技术部','GiGi'
insert into #EnterPrise select '设计组','技术部','yoyo'
insert into #EnterPrise select '专项组','程序组','Yue'
insert into #EnterPrise select '总经办','','Boss'
--查询部门经理是Tom的下面的部门名称
;with hgo as
(
select *,0 as rank from #EnterPrise where DepartManage='Tom'
union all
select h.*,h1.rank+1 from #EnterPrise h join hgo h1 on h.ParentDept=h1.Department
)
select * from hgo
/*
Department ParentDept DepartManage rank
--------------- -------------------- ----------------------- -----------
技术部 总经办 Tom 0
程序组 技术部 GiGi 1
设计组 技术部 yoyo 1
专项组 程序组 Yue 2
*/
--查询部门经理是GiGi的上级部门名称
;with hgo as
(
select *,0 as rank from #EnterPrise where DepartManage='GiGi'
union all
select h.*,h1.rank+1 from #EnterPrise h join hgo h1 on h.Department=h1.ParentDept
)
select * from hgo
/*
Department ParentDept DepartManage rank
-------------------- ---------------------- ----------- -----------
程序组 技术部 GiGi 0
技术部 总经办 Tom 1
总经办 Boss 2
*/
USE tempdb
GO
-- 建立演示环境
CREATE TABLE Dept(
id int PRIMARY KEY,
parent_id int,
name nvarchar(20))
INSERT Dept
SELECT 0, 0, N'<全部>' UNION ALL
SELECT 1, 0, N'财务部' UNION ALL
SELECT 2, 0, N'行政部' UNION ALL
SELECT 3, 0, N'业务部' UNION ALL
SELECT 4, 0, N'业务部' UNION ALL
SELECT 5, 4, N'销售部' UNION ALL
SELECT 6, 4, N'MIS' UNION ALL
SELECT 7, 6, N'UI' UNION ALL
SELECT 8, 6, N'软件开发' UNION ALL
SELECT 9, 8, N'内部开发'
GO
-- 查询指定部门下面的所有部门
DECLARE @Dept_name nvarchar(20)
SET @Dept_name = N'MIS'
;WITH
DEPTS AS(
-- 定位点成员
SELECT * FROM Dept
WHERE name = @Dept_name
UNION ALL
-- 递归成员, 通过引用CTE自身与Dept基表JOIN实现递归
SELECT A.*
FROM Dept A, DEPTS B
WHERE A.parent_id = B.id
)
SELECT * FROM DEPTS
GO
-- 删除演示环境
DROP TABLE Dept
----CTE的综合应用
USE tempdb
GO
-- 建立演示环境
CREATE TABLE Dept(
id int PRIMARY KEY,
parent_id int,
name nvarchar(20))
INSERT Dept
SELECT 0, 0, N'<全部>' UNION ALL
SELECT 1, 0, N'财务部' UNION ALL
SELECT 2, 0, N'行政部' UNION ALL
SELECT 3, 0, N'业务部' UNION ALL
SELECT 4, 0, N'业务部' UNION ALL
SELECT 5, 4, N'销售部' UNION ALL
SELECT 6, 4, N'MIS' UNION ALL
SELECT 7, 6, N'UI' UNION ALL
SELECT 8, 6, N'软件开发' UNION ALL
SELECT 9, 8, N'内部开发'
GO
-- 查询指定部门下面的所有部门, 并汇总各部门的下级部门数
DECLARE @Dept_name nvarchar(20)
SET @Dept_name = N'MIS'
;WITH
DEPTS AS( -- 查询指定部门及其下的所有子部门
-- 定位点成员
SELECT * FROM Dept
WHERE name = @Dept_name
UNION ALL
-- 递归成员, 通过引用CTE自身与Dept基表JOIN实现递归
SELECT A.*
FROM Dept A, DEPTS B
WHERE A.parent_id = B.id
),
DEPTCHILD AS( -- 引用第1个CTE,查询其每条记录对应的部门下的所有子部门
SELECT
Dept_id = P.id, C.id, C.parent_id
FROM DEPTS P, Dept C
WHERE P.id = C.parent_id
UNION ALL
SELECT
P.Dept_id, C.id, C.parent_id
FROM DEPTCHILD P, Dept C
WHERE P.id = C.parent_id
),
DEPTCHILDCNT AS( -- 引用第2个CTE, 汇总得到各部门下的子部门数
SELECT
Dept_id, Cnt = COUNT(*)
FROM DEPTCHILD
GROUP BY Dept_id
)
SELECT -- JOIN第1,3个CTE,得到最终的查询结果
D.*,
ChildDeptCount = ISNULL(DS.Cnt, 0)
FROM DEPTS D
LEFT JOIN DEPTCHILDCNT DS
ON D.id = DS.Dept_id
GO
-- 删除演示环境
DROP TABLE Dept
| 引用(0)
实验环境:本环境一共七台服务器,操作系统均为CentOS 5.3 x86_64
有4 种角色,分别为keepalived 负载均衡器,sql 节点,数据节点,集群管理节点
其中数据库版本为mysql-cluster-gpl-7.0.8a-linux-x86_64-glibc23.tar.gz,一个管理节点2 个存储节点,2 个sql 节点
2 台负载均衡器的版本为keepalived-1.1.17.tar.gz,ipvsadm-1.24.tar.gz
整个架构对外只提供vip 的服务,采用的rr 轮询的方式来访问sql 节点,同时实现了负载均衡和高可用
ip 的对应关系如下:
Manage node:192.168.10.187
Data node-1:192.168.10.184
Data node-2:192.168.10.186
Sql node-1:192.168.10.175
Sql node-2:192.168.10.183
Keepalived_1:192.168.10.170
Keepalived_2:192.168.10.176
VIP:192.168.10.60
具体搭建过程如下:
1. 首先配置数据库集群环境
管理的节点
(1)安装管理节点
[root@qubaoquan local]# tar zvxf
mysql-cluster-gpl-7.1.4b-linux-x86_64-glibc23.tar.gz
[root@qubaoquan local]# mv mysql-cluster-gpl-7.1.4b-linux-x86_64-glibc23 mysql
[root@qubaoquan local]# cd /usr/local/
[root@qubaoquan local]# chown -R mysql:mysql mysql
[root@qubaoquan local]# cd mysql
[root@qubaoquan mysql]# ./scripts/mysql_install_db --user=mysql
[root@qubaoquan mysql]# mkdir mysql-cluster
(2)配置
[root@qubaoquan mysql]# cd mysql-cluster/
[root@qubaoquan mysql-cluster]# vi config.ini(内容如下)
[tcp default]
PortNumber= 63132
[ndbd default]
NoOfReplicas= 2
MaxNoOfConcurrentOperations= 10000
DataMemory= 80M
IndexMemory= 24M
TimeBetweenWatchDogCheck= 30000
DataDir= /usr/local/mysql/data
MaxNoOfOrderedIndexes= 512
[ndb_mgmd]
Id= 1
HostName= 192.168.10.187
DataDir= /usr/local/mysql/data
[ndbd]
Id= 2
HostName= 192.168.10.184
DataDir= /usr/local/mysql/data
[ndbd]
Id= 3
HostName= 192.168.10.186
DataDir= /usr/local/mysql/data
[mysqld]
Id= 4
HostName= 192.168.10.175
[mysqld]
Id= 5
HostName= 192.168.10.183
[root@inas-3 mysql]# chown -R mysql:mysql /usr/local/mysql
数据节点(2 个节点的安装和配置完全一致)
安装
[root@qubaoquan local]# tar zvxf
mysql-cluster-gpl-7.1.4b-linux-x86_64-glibc23.tar.gz
[root@qubaoquan local]# mv mysql-cluster-gpl-7.1.4b-linux-x86_64-glibc23 mysql
[root@qubaoquan local]# cd /usr/local/
[root@qubaoquan local]# chown -R mysql:mysql mysql
[root@qubaoquan local]# cd mysql
[root@qubaoquan mysql]# ./scripts/mysql_install_db --user=mysql
[root@inas-3 mysql]# chown -R mysql:mysql /usr/local/mysql
(2)配置
[root@inas-3 mysql]# cp support-files/my-medium.cnf /etc/my.cnf
[root@inas-3 mysql]# vi /etc/my.cnf(加到文件末尾)
################ cluster #########################
datadir=/usr/local/mysql/data/
ndbcluster
ndb-connectstring=192.168.10.187
[ndbd]
connect-string=192.168.10.187
[mysql_cluster]
ndb-connectstring=192.168.10.187
[ndb_mgm]
connect-string=192.168.10.187
[ndb_mgmd]
config-file=/usr/local/mysql/mysql-cluster/config.ini
Sql 节点
安装
[root@qubaoquan local]# tar zvxf
mysql-cluster-gpl-7.1.4b-linux-x86_64-glibc23.tar.gz
[root@qubaoquan local]# mv mysql-cluster-gpl-7.1.4b-linux-x86_64-glibc23 mysql
[root@qubaoquan local]# cd /usr/local/
[root@qubaoquan local]# chown -R mysql:mysql mysql
[root@qubaoquan local]# cd mysql
[root@qubaoquan mysql]# ./scripts/mysql_install_db --user=mysql
(2)配置
[root@inas-3 mysql]# cp support-files/my-medium.cnf /etc/my.cnf
[root@inas-3 mysql]# cp support-files/mysql.server /etc/init.d/mysqld
[root@inas-3 mysql]# chkconfig --add mysqld
[root@inas-3 mysql]# chkconfig --levles mysqld on
[root@gs173 mysql]# vi /etc/my.cnf(添加到mysqld 标签内)
############# cluster #################
ndbcluster
ndb-connectstring=192.168.10.187
分别启动服务
启动管理节点
[root@qubaoquan bin]# ./ndb_mgmd -f /usr/local/mysql/mysql-cluster/config.ini
[root@qubaoquan bin]# netstat -nltp |grep 1186
tcp 0 0 0.0.0.0:1186 0.0.0.0:*
LISTEN 11846/ndb_mgmd
启动数据节点
[root@inas-3 bin]# ./ndbd –initial
[root@inas-3 bin]# netstat -nltp |grep 63132
tcp 0 0 192.168.10.184:63132 0.0.0.0:*
LISTEN 1589/ndbd
启动sql 节点
[root@googleserver177 mysql]# /etc/init.d/mysqld start
[root@googleserver177 mysql]# netstat -nltp |grep 3306
tcp 0 0 :::3306 :::*
LISTEN 13193/mysqld
备注:
(1)启动管理节点:ndb_mgmd --initial -f /usr/local/mysql/mysql-cluster/config.ini
(first
time)
ndb_mgmd -f /usr/local/mysql/mysql-cluster/config.ini (not first time)
(2)启动数据节点:bin/ndbd --initial (first time)
bin/ndbd (not firest time)
//以下方式也可启动数据节点
bin/ndbd --initial -c localhost:1186
bin/ndbd --initial -c ip:1186
bin/ndbd --defaults-file=/etc/my.cnf --initial
(3)关闭管理节点: ndb_mgm -e shutdown
( 4 ) 查看工作状态:
在管理节点上运行[root@qubaoquan bin]# /usr/local/mysql/bin/ndb_mgm
例如:
[root@qubaoquan bin]# /usr/local/mysql/bin/ndb_mgm
-- NDB Cluster -- Management Client --
ndb_mgm> show
Cluster Configuration
---------------------
[ndbd(NDB)] 2 node(s)
id=2 @192.168.10.184 (mysql-5.1.44 ndb-7.1.4, Nodegroup: )
id=3 @192.168.10.186 (mysql-5.1.44 ndb-7.1.4, Nodegroup: 0, Master)
[ndb_mgmd(MGM)] 1 node(s)
id=1 @192.168.10.187 (mysql-5.1.44 ndb-7.1.4)
[mysqld(API)] 2 node(s)
id=4 @192.168.10.175 (mysql-5.1.44 ndb-7.1.4)
id=5 @192.168.10.183 (mysql-5.1.44 ndb-7.1.4)
ndb_mgm>
2. keepalived 的安装和配置,如下操作都在两个负载均衡节点上操作
主控制节点的配置
(1) 首先连接内核文件
[root@master srv]# ln -s /usr/src/kernels/2.6.18-128.el5-x86_64/ /usr/src/linux/
(2) 安装ipvs 核心
[root@master srv]# tar vzxf libnet-1.1.2.1.tar.gz
[root@master srv]# cd libnet
[root@master libnet]# ./configure
[root@master libnet]# make
[root@master libnet]# make install
[root@master srv]# tar zvxf ipvsadm-1.24.tar.gz
[root@master srv]# cd ipvsadm-1.24
[root@master ipvsadm-1.24]#
[root@master ipvsadm-1.24]# make
[root@master ipvsadm-1.24]# make install
(3) 安装和配置keepalived
[root@master ipvsadm-1.24]# cd ..
[root@master srv]# tar vzxf keepalived-1.1.17.tar.gz
[root@master srv]# cd keepalived-1.1.17
[root@master keepalived-1.1.17]# ./configure ¨Cprefix=/usr/local/keepalive
[root@master keepalived-1.1.17]# make
[root@master keepalived-1.1.17]# make install
[root@master keepalived-1.1.17]# mkdir -p /etc/keepalived/
[root@master keepalived-1.1.17]# vi /etc/keepalived/keepalived.conf(内容如下)
#global define
global_defs {
router_id 1
}
vrrp_sync_group test {
group {
test_1
}
}
################################################################
# vvrp_instance define #
################################################################
vrrp_instance test_1 {
state MASTER
interface eth0
lvs_sync_daemon_inteface eth0
virtual_router_id 10
priority 180
advert_int 5
authentication {
auth_type PASS
auth_pass 1111
}
virtual_ipaddress {
192.168.10.60
}
}
######################################################################
#####################################################
# virtual machine setting #
##############################################################
# setting port 3306 forward
virtual_server 192.168.10.60 3306 {
delay_loop 6
lb_algo rr
lb_kind DR
# persistence_timeout 20
protocol TCP
real_server 192.168.10.175 3306 {
weight 100
TCP_CHECK {
connect_timeout 3
nb_get_retry 3
delay_before_retry 3
connect_port 3306
}
}
real_server 192.168.10.183 3306 {
weight 100
TCP_CHECK {
connect_timeout 3
nb_get_retry 3
delay_before_retry 3
connect_port 3306
}
}
}
(4)注册为系统服务
[root@master keepalived-1.1.17]# cp keepalived/etc/init.d/keepalived.rh.init /etc/init.d/keepalived
[root@master keepalived-1.1.17]# chmod +x /etc/init.d/keepalived
[root@master keepalived-1.1.17]# chkconfig --add keepalived
[root@master keepalived-1.1.17]# chkconfig --levels 2345 keepalived on
[root@master keepalived-1.1.17]# cp /usr/local/keepalive/sbin/keepalived /usr/bin
辅助控制节点的配置
(1)首先连接内核文件
[root@master srv]# ln -s /usr/src/kernels/2.6.18-128.el5-x86_64/ /usr/src/linux/
(2) 安装ipvs 核心
[root@master srv]# tar vzxf libnet-1.1.2.1.tar.gz
[root@master srv]# cd libnet
[root@master libnet]# ./configure
[root@master libnet]# make
[root@master libnet]# make install
[root@master srv]# tar zvxf ipvsadm-1.24.tar.gz
[root@master srv]# cd ipvsadm-1.24
[root@master ipvsadm-1.24]#
[root@master ipvsadm-1.24]# make
[root@master ipvsadm-1.24]# make install
(3) 安装和配置keepalived
[root@master ipvsadm-1.24]# cd ..
[root@master srv]# tar vzxf keepalived-1.1.17.tar.gz
[root@master srv]# cd keepalived-1.1.17
[root@master keepalived-1.1.17]# ./configure ¨Cprefix=/usr/local/keepalive
[root@master keepalived-1.1.17]# make
[root@master keepalived-1.1.17]# make install
[root@master keepalived-1.1.17]# mkdir -p /etc/keepalived/
[root@master keepalived-1.1.17]# vi /etc/keepalived/keepalived.conf(内容如下)
#global define
global_defs {
router_id 2
}
vrrp_sync_group test {
group {
test_1
}
}
######################################################################
#########################################################
# vrrp_instance setting #
################################################################
vrrp_instance test_1 {
state BACKUP
interface eth0
lvs_sync_daemon_inteface eth0
virtual_router_id 20
priority 150
advert_int 5
authentication {
auth_type PASS
auth_pass 1111
}
virtual_ipaddress {
192.168.10.60
}
}
##############################################################
# virtual server setting #
##############################################################
# setting port 3306 forward
virtual_server 192.168.10.60 3306 {
delay_loop 6
lb_algo rr
lb_kind DR
# persistence_timeout 50
protocol TCP
real_server 192.168.10.175 3306 {
weight 100
TCP_CHECK {
connect_timeout 3
nb_get_retry 3
delay_before_retry 3
connect_port 3306
}
}
real_server 192.168.10.183 3306 {
weight 100
TCP_CHECK {
connect_timeout 3
nb_get_retry 3
delay_before_retry 3
connect_port 3306
}
}
}
(4)注册为系统服务
[root@master keepalived-1.1.17]# cp keepalived/etc/init.d/keepalived.rh.init /etc/init.d/keepalived
[root@master keepalived-1.1.17]# chmod +x /etc/init.d/keepalived
[root@master keepalived-1.1.17]# chkconfig --add keepalived
[root@master keepalived-1.1.17]# chkconfig --levels 2345 keepalived on
[root@master keepalived-1.1.17]# cp /usr/local/keepalive/sbin/keepalived /usr/bin
}
3. realserver的配置,如下操作都在两个sql节点上操作(操作完全相同本处只以sql_1为例)
[root@localhost ~]# vi /etc/init.d/realserver(内容如下)
#!/bin/bash
#description : start realserver
# chkconfig: 37 22 80
VIP=192.168.10.60
/etc/rc.d/init.d/functions
case "$1" in
start)
echo " start LVS of REALServer"
/sbin/ifconfig lo:0 $VIP broadcast $VIP netmask 255.255.255.255 up
echo "1" >/proc/sys/net/ipv4/conf/lo/arp_ignore
echo "2" >/proc/sys/net/ipv4/conf/lo/arp_announce
echo "1" >/proc/sys/net/ipv4/conf/all/arp_ignore
echo "2" >/proc/sys/net/ipv4/conf/all/arp_announce
;;
stop)
/sbin/ifconfig lo:0 down
echo "close LVS Directorserver"
echo "0" >/proc/sys/net/ipv4/conf/lo/arp_ignore
echo "0" >/proc/sys/net/ipv4/conf/lo/arp_announce
echo "0" >/proc/sys/net/ipv4/conf/all/arp_ignore
echo "0" >/proc/sys/net/ipv4/conf/all/arp_announce
;;
*)
echo "Usage: $0 {start|stop}"
exit 1
esac
[root@localhost ~]# chmod +x /etc/init.d/realserver
[root@localhost ~]# chkconfig --add realserver
[root@localhost ~]# chkconfig --levels 2345 realserver on
启动服务
(1) 启动两个realserver的脚本
[root@localhost ~]# /etc/init.d/realserver start
start LVS of REALServer
在sql节点上查看虚拟ip是否已经出现
[root@localhost ~]# ifconfig -a
eth0 Link encap:Ethernet HWaddr 00:14:85:EC:F7:52
inet addr:192.168.10.183 Bcast:192.168.10.255
Mask:255.255.255.0
inet6 addr: fe80::214:85ff:feec:f752/64 Scope:Link
UP BROADCAST RUNNING MULTICAST
MTU:1500 Metric:1
RX packets:310359 errors:0 dropped:0 overruns:0
frame:0
TX packets:160995 errors:0 dropped:0 overruns:0
carrier:0
collisions:0 txqueuelen:1000
RX bytes:28207983 (26.9 MiB) TX bytes:16492745
(15.7 MiB)
Interrupt:169
lo Link encap:Local Loopback
inet addr:127.0.0.1 Mask:255.0.0.0
inet6 addr: ::1/128 Scope:Host
UP LOOPBACK RUNNING MTU:16436 Metric:1
RX packets:48727 errors:0 dropped:0 overruns:0
frame:0
TX packets:48727 errors:0 dropped:0 overruns:0
carrier:0
collisions:0 txqueuelen:0
RX bytes:3776120 (3.6 MiB) TX bytes:3776120 (3.6
MiB)
lo:0 Link encap:Local Loopback
inet addr:192.168.10.6 Mask:255.255.255.255
UP LOOPBACK RUNNING MTU:16436 Metric:1
sit0 Link encap:IPv6-in-IPv4
NOARP MTU:1480 Metric:1
RX packets:0 errors:0 dropped:0 overruns:0
frame:0
TX packets:0 errors:0 dropped:0 overruns:0
carrier:0
collisions:0 txqueuelen:0
RX bytes:0 (0.0 b) TX bytes:0 (0.0 b)
[root@localhost ~]#
(2)启动两个控制节点的服务
[root@qubaoquan ~]# /etc/init.d/keepalived start
在控制节点上查看虚拟ip是否已经出现
[root@master keepalived-1.1.17]# ip addr
1: lo: mtu 16436 qdisc noqueue
link/loopback 00:00:00:00:00:00 brd 00:00:00:00:00:00
inet 127.0.0.1/8 scope host lo
inet6 ::1/128 scope host
valid_lft forever preferred_lft forever
2: eth0: mtu 1500
qdisc pfifo_fast qlen 1000
link/ether 00:14:85:ec:f2:81 brd ff:ff:ff:ff:ff:ff
inet 192.168.10.170/24 brd 192.168.10.255 scope global eth0
inet 192.168.10.60/32 scope global eth0
inet6 fe80::214:85ff:feec:f281/64 scope link
valid_lft forever preferred_lft forever
3: sit0: mtu 1480 qdisc noop
link/sit 0.0.0.0 brd 0.0.0.0
在控制节点上查看主机列表
[root@master keepalived-1.1.17]# ipvsadm -L
IP Virtual Server version 1.2.1 (size=4096)
Prot LocalAddress:Port Scheduler Flags
-> RemoteAddress:Port Forward Weight
ActiveConn InActConn
TCP 192.168.10.60:mysql rr
-> 192.168.10.183:mysql Route 100 0 0
-> 192.168.10.175:mysql Route 100 0 0
}
有4 种角色,分别为keepalived 负载均衡器,sql 节点,数据节点,集群管理节点
其中数据库版本为mysql-cluster-gpl-7.0.8a-linux-x86_64-glibc23.tar.gz,一个管理节点2 个存储节点,2 个sql 节点
2 台负载均衡器的版本为keepalived-1.1.17.tar.gz,ipvsadm-1.24.tar.gz
整个架构对外只提供vip 的服务,采用的rr 轮询的方式来访问sql 节点,同时实现了负载均衡和高可用
ip 的对应关系如下:
Manage node:192.168.10.187
Data node-1:192.168.10.184
Data node-2:192.168.10.186
Sql node-1:192.168.10.175
Sql node-2:192.168.10.183
Keepalived_1:192.168.10.170
Keepalived_2:192.168.10.176
VIP:192.168.10.60
具体搭建过程如下:
1. 首先配置数据库集群环境
管理的节点
(1)安装管理节点
[root@qubaoquan local]# tar zvxf
mysql-cluster-gpl-7.1.4b-linux-x86_64-glibc23.tar.gz
[root@qubaoquan local]# mv mysql-cluster-gpl-7.1.4b-linux-x86_64-glibc23 mysql
[root@qubaoquan local]# cd /usr/local/
[root@qubaoquan local]# chown -R mysql:mysql mysql
[root@qubaoquan local]# cd mysql
[root@qubaoquan mysql]# ./scripts/mysql_install_db --user=mysql
[root@qubaoquan mysql]# mkdir mysql-cluster
(2)配置
[root@qubaoquan mysql]# cd mysql-cluster/
[root@qubaoquan mysql-cluster]# vi config.ini(内容如下)
[tcp default]
PortNumber= 63132
[ndbd default]
NoOfReplicas= 2
MaxNoOfConcurrentOperations= 10000
DataMemory= 80M
IndexMemory= 24M
TimeBetweenWatchDogCheck= 30000
DataDir= /usr/local/mysql/data
MaxNoOfOrderedIndexes= 512
[ndb_mgmd]
Id= 1
HostName= 192.168.10.187
DataDir= /usr/local/mysql/data
[ndbd]
Id= 2
HostName= 192.168.10.184
DataDir= /usr/local/mysql/data
[ndbd]
Id= 3
HostName= 192.168.10.186
DataDir= /usr/local/mysql/data
[mysqld]
Id= 4
HostName= 192.168.10.175
[mysqld]
Id= 5
HostName= 192.168.10.183
[root@inas-3 mysql]# chown -R mysql:mysql /usr/local/mysql
数据节点(2 个节点的安装和配置完全一致)
安装
[root@qubaoquan local]# tar zvxf
mysql-cluster-gpl-7.1.4b-linux-x86_64-glibc23.tar.gz
[root@qubaoquan local]# mv mysql-cluster-gpl-7.1.4b-linux-x86_64-glibc23 mysql
[root@qubaoquan local]# cd /usr/local/
[root@qubaoquan local]# chown -R mysql:mysql mysql
[root@qubaoquan local]# cd mysql
[root@qubaoquan mysql]# ./scripts/mysql_install_db --user=mysql
[root@inas-3 mysql]# chown -R mysql:mysql /usr/local/mysql
(2)配置
[root@inas-3 mysql]# cp support-files/my-medium.cnf /etc/my.cnf
[root@inas-3 mysql]# vi /etc/my.cnf(加到文件末尾)
################ cluster #########################
datadir=/usr/local/mysql/data/
ndbcluster
ndb-connectstring=192.168.10.187
[ndbd]
connect-string=192.168.10.187
[mysql_cluster]
ndb-connectstring=192.168.10.187
[ndb_mgm]
connect-string=192.168.10.187
[ndb_mgmd]
config-file=/usr/local/mysql/mysql-cluster/config.ini
Sql 节点
安装
[root@qubaoquan local]# tar zvxf
mysql-cluster-gpl-7.1.4b-linux-x86_64-glibc23.tar.gz
[root@qubaoquan local]# mv mysql-cluster-gpl-7.1.4b-linux-x86_64-glibc23 mysql
[root@qubaoquan local]# cd /usr/local/
[root@qubaoquan local]# chown -R mysql:mysql mysql
[root@qubaoquan local]# cd mysql
[root@qubaoquan mysql]# ./scripts/mysql_install_db --user=mysql
(2)配置
[root@inas-3 mysql]# cp support-files/my-medium.cnf /etc/my.cnf
[root@inas-3 mysql]# cp support-files/mysql.server /etc/init.d/mysqld
[root@inas-3 mysql]# chkconfig --add mysqld
[root@inas-3 mysql]# chkconfig --levles mysqld on
[root@gs173 mysql]# vi /etc/my.cnf(添加到mysqld 标签内)
############# cluster #################
ndbcluster
ndb-connectstring=192.168.10.187
分别启动服务
启动管理节点
[root@qubaoquan bin]# ./ndb_mgmd -f /usr/local/mysql/mysql-cluster/config.ini
[root@qubaoquan bin]# netstat -nltp |grep 1186
tcp 0 0 0.0.0.0:1186 0.0.0.0:*
LISTEN 11846/ndb_mgmd
启动数据节点
[root@inas-3 bin]# ./ndbd –initial
[root@inas-3 bin]# netstat -nltp |grep 63132
tcp 0 0 192.168.10.184:63132 0.0.0.0:*
LISTEN 1589/ndbd
启动sql 节点
[root@googleserver177 mysql]# /etc/init.d/mysqld start
[root@googleserver177 mysql]# netstat -nltp |grep 3306
tcp 0 0 :::3306 :::*
LISTEN 13193/mysqld
备注:
(1)启动管理节点:ndb_mgmd --initial -f /usr/local/mysql/mysql-cluster/config.ini
(first
time)
ndb_mgmd -f /usr/local/mysql/mysql-cluster/config.ini (not first time)
(2)启动数据节点:bin/ndbd --initial (first time)
bin/ndbd (not firest time)
//以下方式也可启动数据节点
bin/ndbd --initial -c localhost:1186
bin/ndbd --initial -c ip:1186
bin/ndbd --defaults-file=/etc/my.cnf --initial
(3)关闭管理节点: ndb_mgm -e shutdown
( 4 ) 查看工作状态:
在管理节点上运行[root@qubaoquan bin]# /usr/local/mysql/bin/ndb_mgm
例如:
[root@qubaoquan bin]# /usr/local/mysql/bin/ndb_mgm
-- NDB Cluster -- Management Client --
ndb_mgm> show
Cluster Configuration
---------------------
[ndbd(NDB)] 2 node(s)
id=2 @192.168.10.184 (mysql-5.1.44 ndb-7.1.4, Nodegroup: )
id=3 @192.168.10.186 (mysql-5.1.44 ndb-7.1.4, Nodegroup: 0, Master)
[ndb_mgmd(MGM)] 1 node(s)
id=1 @192.168.10.187 (mysql-5.1.44 ndb-7.1.4)
[mysqld(API)] 2 node(s)
id=4 @192.168.10.175 (mysql-5.1.44 ndb-7.1.4)
id=5 @192.168.10.183 (mysql-5.1.44 ndb-7.1.4)
ndb_mgm>
2. keepalived 的安装和配置,如下操作都在两个负载均衡节点上操作
主控制节点的配置
(1) 首先连接内核文件
[root@master srv]# ln -s /usr/src/kernels/2.6.18-128.el5-x86_64/ /usr/src/linux/
(2) 安装ipvs 核心
[root@master srv]# tar vzxf libnet-1.1.2.1.tar.gz
[root@master srv]# cd libnet
[root@master libnet]# ./configure
[root@master libnet]# make
[root@master libnet]# make install
[root@master srv]# tar zvxf ipvsadm-1.24.tar.gz
[root@master srv]# cd ipvsadm-1.24
[root@master ipvsadm-1.24]#
[root@master ipvsadm-1.24]# make
[root@master ipvsadm-1.24]# make install
(3) 安装和配置keepalived
[root@master ipvsadm-1.24]# cd ..
[root@master srv]# tar vzxf keepalived-1.1.17.tar.gz
[root@master srv]# cd keepalived-1.1.17
[root@master keepalived-1.1.17]# ./configure ¨Cprefix=/usr/local/keepalive
[root@master keepalived-1.1.17]# make
[root@master keepalived-1.1.17]# make install
[root@master keepalived-1.1.17]# mkdir -p /etc/keepalived/
[root@master keepalived-1.1.17]# vi /etc/keepalived/keepalived.conf(内容如下)
#global define
global_defs {
router_id 1
}
vrrp_sync_group test {
group {
test_1
}
}
################################################################
# vvrp_instance define #
################################################################
vrrp_instance test_1 {
state MASTER
interface eth0
lvs_sync_daemon_inteface eth0
virtual_router_id 10
priority 180
advert_int 5
authentication {
auth_type PASS
auth_pass 1111
}
virtual_ipaddress {
192.168.10.60
}
}
######################################################################
#####################################################
# virtual machine setting #
##############################################################
# setting port 3306 forward
virtual_server 192.168.10.60 3306 {
delay_loop 6
lb_algo rr
lb_kind DR
# persistence_timeout 20
protocol TCP
real_server 192.168.10.175 3306 {
weight 100
TCP_CHECK {
connect_timeout 3
nb_get_retry 3
delay_before_retry 3
connect_port 3306
}
}
real_server 192.168.10.183 3306 {
weight 100
TCP_CHECK {
connect_timeout 3
nb_get_retry 3
delay_before_retry 3
connect_port 3306
}
}
}
(4)注册为系统服务
[root@master keepalived-1.1.17]# cp keepalived/etc/init.d/keepalived.rh.init /etc/init.d/keepalived
[root@master keepalived-1.1.17]# chmod +x /etc/init.d/keepalived
[root@master keepalived-1.1.17]# chkconfig --add keepalived
[root@master keepalived-1.1.17]# chkconfig --levels 2345 keepalived on
[root@master keepalived-1.1.17]# cp /usr/local/keepalive/sbin/keepalived /usr/bin
辅助控制节点的配置
(1)首先连接内核文件
[root@master srv]# ln -s /usr/src/kernels/2.6.18-128.el5-x86_64/ /usr/src/linux/
(2) 安装ipvs 核心
[root@master srv]# tar vzxf libnet-1.1.2.1.tar.gz
[root@master srv]# cd libnet
[root@master libnet]# ./configure
[root@master libnet]# make
[root@master libnet]# make install
[root@master srv]# tar zvxf ipvsadm-1.24.tar.gz
[root@master srv]# cd ipvsadm-1.24
[root@master ipvsadm-1.24]#
[root@master ipvsadm-1.24]# make
[root@master ipvsadm-1.24]# make install
(3) 安装和配置keepalived
[root@master ipvsadm-1.24]# cd ..
[root@master srv]# tar vzxf keepalived-1.1.17.tar.gz
[root@master srv]# cd keepalived-1.1.17
[root@master keepalived-1.1.17]# ./configure ¨Cprefix=/usr/local/keepalive
[root@master keepalived-1.1.17]# make
[root@master keepalived-1.1.17]# make install
[root@master keepalived-1.1.17]# mkdir -p /etc/keepalived/
[root@master keepalived-1.1.17]# vi /etc/keepalived/keepalived.conf(内容如下)
#global define
global_defs {
router_id 2
}
vrrp_sync_group test {
group {
test_1
}
}
######################################################################
#########################################################
# vrrp_instance setting #
################################################################
vrrp_instance test_1 {
state BACKUP
interface eth0
lvs_sync_daemon_inteface eth0
virtual_router_id 20
priority 150
advert_int 5
authentication {
auth_type PASS
auth_pass 1111
}
virtual_ipaddress {
192.168.10.60
}
}
##############################################################
# virtual server setting #
##############################################################
# setting port 3306 forward
virtual_server 192.168.10.60 3306 {
delay_loop 6
lb_algo rr
lb_kind DR
# persistence_timeout 50
protocol TCP
real_server 192.168.10.175 3306 {
weight 100
TCP_CHECK {
connect_timeout 3
nb_get_retry 3
delay_before_retry 3
connect_port 3306
}
}
real_server 192.168.10.183 3306 {
weight 100
TCP_CHECK {
connect_timeout 3
nb_get_retry 3
delay_before_retry 3
connect_port 3306
}
}
}
(4)注册为系统服务
[root@master keepalived-1.1.17]# cp keepalived/etc/init.d/keepalived.rh.init /etc/init.d/keepalived
[root@master keepalived-1.1.17]# chmod +x /etc/init.d/keepalived
[root@master keepalived-1.1.17]# chkconfig --add keepalived
[root@master keepalived-1.1.17]# chkconfig --levels 2345 keepalived on
[root@master keepalived-1.1.17]# cp /usr/local/keepalive/sbin/keepalived /usr/bin
}
3. realserver的配置,如下操作都在两个sql节点上操作(操作完全相同本处只以sql_1为例)
[root@localhost ~]# vi /etc/init.d/realserver(内容如下)
#!/bin/bash
#description : start realserver
# chkconfig: 37 22 80
VIP=192.168.10.60
/etc/rc.d/init.d/functions
case "$1" in
start)
echo " start LVS of REALServer"
/sbin/ifconfig lo:0 $VIP broadcast $VIP netmask 255.255.255.255 up
echo "1" >/proc/sys/net/ipv4/conf/lo/arp_ignore
echo "2" >/proc/sys/net/ipv4/conf/lo/arp_announce
echo "1" >/proc/sys/net/ipv4/conf/all/arp_ignore
echo "2" >/proc/sys/net/ipv4/conf/all/arp_announce
;;
stop)
/sbin/ifconfig lo:0 down
echo "close LVS Directorserver"
echo "0" >/proc/sys/net/ipv4/conf/lo/arp_ignore
echo "0" >/proc/sys/net/ipv4/conf/lo/arp_announce
echo "0" >/proc/sys/net/ipv4/conf/all/arp_ignore
echo "0" >/proc/sys/net/ipv4/conf/all/arp_announce
;;
*)
echo "Usage: $0 {start|stop}"
exit 1
esac
[root@localhost ~]# chmod +x /etc/init.d/realserver
[root@localhost ~]# chkconfig --add realserver
[root@localhost ~]# chkconfig --levels 2345 realserver on
启动服务
(1) 启动两个realserver的脚本
[root@localhost ~]# /etc/init.d/realserver start
start LVS of REALServer
在sql节点上查看虚拟ip是否已经出现
[root@localhost ~]# ifconfig -a
eth0 Link encap:Ethernet HWaddr 00:14:85:EC:F7:52
inet addr:192.168.10.183 Bcast:192.168.10.255
Mask:255.255.255.0
inet6 addr: fe80::214:85ff:feec:f752/64 Scope:Link
UP BROADCAST RUNNING MULTICAST
MTU:1500 Metric:1
RX packets:310359 errors:0 dropped:0 overruns:0
frame:0
TX packets:160995 errors:0 dropped:0 overruns:0
carrier:0
collisions:0 txqueuelen:1000
RX bytes:28207983 (26.9 MiB) TX bytes:16492745
(15.7 MiB)
Interrupt:169
lo Link encap:Local Loopback
inet addr:127.0.0.1 Mask:255.0.0.0
inet6 addr: ::1/128 Scope:Host
UP LOOPBACK RUNNING MTU:16436 Metric:1
RX packets:48727 errors:0 dropped:0 overruns:0
frame:0
TX packets:48727 errors:0 dropped:0 overruns:0
carrier:0
collisions:0 txqueuelen:0
RX bytes:3776120 (3.6 MiB) TX bytes:3776120 (3.6
MiB)
lo:0 Link encap:Local Loopback
inet addr:192.168.10.6 Mask:255.255.255.255
UP LOOPBACK RUNNING MTU:16436 Metric:1
sit0 Link encap:IPv6-in-IPv4
NOARP MTU:1480 Metric:1
RX packets:0 errors:0 dropped:0 overruns:0
frame:0
TX packets:0 errors:0 dropped:0 overruns:0
carrier:0
collisions:0 txqueuelen:0
RX bytes:0 (0.0 b) TX bytes:0 (0.0 b)
[root@localhost ~]#
(2)启动两个控制节点的服务
[root@qubaoquan ~]# /etc/init.d/keepalived start
在控制节点上查看虚拟ip是否已经出现
[root@master keepalived-1.1.17]# ip addr
1: lo:
link/loopback 00:00:00:00:00:00 brd 00:00:00:00:00:00
inet 127.0.0.1/8 scope host lo
inet6 ::1/128 scope host
valid_lft forever preferred_lft forever
2: eth0:
qdisc pfifo_fast qlen 1000
link/ether 00:14:85:ec:f2:81 brd ff:ff:ff:ff:ff:ff
inet 192.168.10.170/24 brd 192.168.10.255 scope global eth0
inet 192.168.10.60/32 scope global eth0
inet6 fe80::214:85ff:feec:f281/64 scope link
valid_lft forever preferred_lft forever
3: sit0:
link/sit 0.0.0.0 brd 0.0.0.0
在控制节点上查看主机列表
[root@master keepalived-1.1.17]# ipvsadm -L
IP Virtual Server version 1.2.1 (size=4096)
Prot LocalAddress:Port Scheduler Flags
-> RemoteAddress:Port Forward Weight
ActiveConn InActConn
TCP 192.168.10.60:mysql rr
-> 192.168.10.183:mysql Route 100 0 0
-> 192.168.10.175:mysql Route 100 0 0
}
| 引用(0)
解决方法:
1。 改表法。
可能是你的帐号不允许从远程登陆,只能在localhost。这个时候只要在localhost的那台电脑,登入mysql后,更改 "mysql" 数据库里的 "user" 表里的 "host" 项,从"localhost"改称"%"
mysql -u root -pvmwaremysql>use mysql;
mysql>update user set host = '%' where user = 'root';
mysql>select host, user from user;
mysql>FLUSH PRIVILEGES;
2. 授权法。
例如,你想myuser使用mypassword从任何主机连接到mysql服务器的话。
GRANT ALL PRIVILEGES ON *.* TO 'myuser'@'%' IDENTIFIED BY 'mypassword' WITH GRANT OPTION;
FLUSH PRIVILEGES;
如果你想允许用户myuser从ip为192.168.1.6的主机连接到mysql服务器,并使用mypassword作为密码
GRANT ALL PRIVILEGES ON *.* TO 'myuser'@'192.168.1.3' IDENTIFIED BY 'mypassword' WITH GRANT OPTION;
FLUSH PRIVILEGES;
如果你想允许用户myuser从ip为192.168.1.6的主机连接到mysql服务器的dk数据库,并使用mypassword作为密码
GRANT ALL PRIVILEGES ON dk.* TO 'myuser'@'192.168.1.3' IDENTIFIED BY 'mypassword' WITH GRANT OPTION;
FLUSH PRIVILEGES;
我用的第一个方法,刚开始发现不行,在网上查了一下,少执行一个语句 mysql>FLUSH RIVILEGES 使修改生效.就可以了
另外一种方法,不过我没有亲自试过的,在csdn.net上找的,可以看一下.
在安装mysql的机器上运行:
1、d:\mysql\bin\>mysql -h localhost -u root //这样应该可以进入MySQL服务器
2、mysql>GRANT ALL PRIVILEGES ON *.* TO 'root'@'%' WITH GRANT OPTION //赋予任何主机访问数据的权限
3、mysql>FLUSH PRIVILEGES //修改生效
4、mysql>EXIT //退出MySQL服务器
这样就可以在其它任何的主机上以root身份登录啦!
1。 改表法。
可能是你的帐号不允许从远程登陆,只能在localhost。这个时候只要在localhost的那台电脑,登入mysql后,更改 "mysql" 数据库里的 "user" 表里的 "host" 项,从"localhost"改称"%"
mysql -u root -pvmwaremysql>use mysql;
mysql>update user set host = '%' where user = 'root';
mysql>select host, user from user;
mysql>FLUSH PRIVILEGES;
2. 授权法。
例如,你想myuser使用mypassword从任何主机连接到mysql服务器的话。
GRANT ALL PRIVILEGES ON *.* TO 'myuser'@'%' IDENTIFIED BY 'mypassword' WITH GRANT OPTION;
FLUSH PRIVILEGES;
如果你想允许用户myuser从ip为192.168.1.6的主机连接到mysql服务器,并使用mypassword作为密码
GRANT ALL PRIVILEGES ON *.* TO 'myuser'@'192.168.1.3' IDENTIFIED BY 'mypassword' WITH GRANT OPTION;
FLUSH PRIVILEGES;
如果你想允许用户myuser从ip为192.168.1.6的主机连接到mysql服务器的dk数据库,并使用mypassword作为密码
GRANT ALL PRIVILEGES ON dk.* TO 'myuser'@'192.168.1.3' IDENTIFIED BY 'mypassword' WITH GRANT OPTION;
FLUSH PRIVILEGES;
我用的第一个方法,刚开始发现不行,在网上查了一下,少执行一个语句 mysql>FLUSH RIVILEGES 使修改生效.就可以了
另外一种方法,不过我没有亲自试过的,在csdn.net上找的,可以看一下.
在安装mysql的机器上运行:
1、d:\mysql\bin\>mysql -h localhost -u root //这样应该可以进入MySQL服务器
2、mysql>GRANT ALL PRIVILEGES ON *.* TO 'root'@'%' WITH GRANT OPTION //赋予任何主机访问数据的权限
3、mysql>FLUSH PRIVILEGES //修改生效
4、mysql>EXIT //退出MySQL服务器
这样就可以在其它任何的主机上以root身份登录啦!



2011/10/28
08:26
15778



