SQL查询,需要基于cte或set

时间:2014-01-14 10:03:54

标签: sql sql-server-2008

我有一个使用游标的SQL查询,希望将其更快更快,并尝试提高我对新SQL技术的理解。我正在使用SQL Server 2008

将它粘贴在这里,因为SQLFiddle现在看起来很不稳定。

SET NOCOUNT ON
CREATE TABLE Person(
    [Id] [int] IDENTITY(1,1) NOT NULL,
    [Name] [nvarchar](50) NULL, 
    [Gx] [int] NULL,
    [Gy] [int] NULL)

CREATE TABLE Location(
    [ID] [int] IDENTITY(1,1) NOT NULL,  
    [Name] [nvarchar](50) NULL, 
    [Gx] [int] NULL,
    [Gy] [int] NULL)

CREATE TABLE PersonLocation(
    [ID] [int] IDENTITY(1,1) NOT NULL,  
    [PersonId] [int] NULL,
    [LocationId] [int] NULL)


INSERT INTO Person
(Name, Gx, Gy)
VALUES
('Alice',27287, 09641)

INSERT INTO Person
(Name, Gx, Gy)
VALUES
('Bob',54433, 26101)


INSERT INTO Location
(Name, Gx, Gy)
VALUES
('London',53007, 18027)

INSERT INTO Location
(Name, Gx, Gy)
VALUES
('Oxford',45142, 20563)


INSERT INTO PersonLocation
(PersonId, LocationId) 
VALUES
(2,2)

DECLARE @Table TABLE (PersonId int, SetLocation bit, Distance float)
DECLARE @PersonId int, @Gx int = 0, @Gy int = 0
DECLARE Curs CURSOR FOR SELECT Id, Gx, Gy FROM PERSON
OPEN Curs
FETCH NEXT FROM Curs INTO @PersonId, @Gx, @Gy
WHILE @@Fetch_Status = 0
BEGIN
INSERT @Table (PersonId, SetLocation, Distance) 
SELECT 
    @PersonId, 

    CASE WHEN PL.ID > 0 THEN 1 ELSE 0 END,
    ISNULL(SQRT(((L.Gx - @Gx) / 10) * (L.Gx - @Gx) / 10 + ((L.Gy - @Gy) / 10) * (L.Gy - @Gy) / 10) / 10, 0) 
FROM 
    Location L  
        LEFT JOIN PersonLocation PL ON L.ID = PL.LocationId AND PL.PersonId = @PersonId             
WHERE    
    ISNULL(SQRT(((L.Gx - @Gx) / 10) * (L.Gx - @Gx) / 10 + ((L.Gy - @Gy) / 10) * (L.Gy - @Gy) / 10) / 10, 0) < 250

FETCH NEXT FROM Curs INTO @PersonId, @Gx, @Gy
END
CLOSE Curs
DEALLOCATE Curs
DROP TABLE Person
DROP TABLE Location
DROP TABLE PersonLocation

SELECT * FROM @Table

1 个答案:

答案 0 :(得分:1)

这似乎给出了相同的结果。 Sql Fiddle here

WITH cte AS
(
  SELECT
    P.Id AS PersonId,
    CASE WHEN PL.ID > 0 THEN 1 ELSE 0 END AS SetLocation,
    ISNULL(SQRT(((L.Gx - P.Gx) / 10) * (L.Gx - P.Gx) / 10 + ((L.Gy - P.Gy) / 10) * (L.Gy - P.Gy) / 10) / 10, 0) AS Distance
  FROM 
    Person P
    CROSS JOIN Location L  
    LEFT JOIN PersonLocation PL 
        ON p.ID = PL.PersonID  AND 
        L.ID = PL.LocationId
)
SELECT PersonId, SetLocation, Distance
  FROM cte
  WHERE Distance < 250;

基本上,cte用于来自你在光标中所做的连接的project派生列,然后可以在过滤器,进一步连接等中使用投影列。光标具有交叉的效果在人员和位置之间加入,每人至少有一行,如果PersonLocation中有多行,则可能更多。