Sql 当存在多行时,仅返回一行

Sql 当存在多行时,仅返回一行,sql,sql-server,tsql,Sql,Sql Server,Tsql,我正在使用一个数据库来跟踪学校的实地考察信息。查询将在MS SQL 2005服务器上运行。在一些情况下,我的查询将为同一次实地考察返回多行。因此,我要做的是过滤我的结果,以便如果每个TripID返回多行,则仅显示具有最小StartDateTime的行。 我知道我可以用分区和MIN函数做一些事情,但我不知道该怎么做 这是我的密码: SELECT DISTINCT dbo.Trip_TripInformation.RecordID AS TripID, db

我正在使用一个数据库来跟踪学校的实地考察信息。查询将在MS SQL 2005服务器上运行。在一些情况下,我的查询将为同一次实地考察返回多行。因此,我要做的是过滤我的结果,以便如果每个TripID返回多行,则仅显示具有最小StartDateTime的行。

我知道我可以用分区和MIN函数做一些事情,但我不知道该怎么做

这是我的密码:

 SELECT DISTINCT    
        dbo.Trip_TripInformation.RecordID AS TripID,
        dbo.Trip_TripInformation.TripDate,
        Origin.LocationName AS Origin,
        dbo.Trip_TripInformation.OriginDepartureTime AS StartDateTime,
        dbo.Trip_TripInformation.OriginReturnTime AS ReturnDateTime, 
        ISNULL(dbo.Trip_TripInformation.NoOfStudents, 0) AS NumberOfStudents,
        ISNULL(dbo.Trip_TripInformation.NoOfAdults, 0) AS NumberOfAdults,
        ISNULL(dbo.Trip_TripInformation.NoOfStudents, 0) + ISNULL(dbo.Trip_TripInformation.NoOfAdults, 0) AS NumberOfPassengers,
        Destination.LocationName AS Destination,
        dbo.Vehicles.Vehicle,
        Driver.LastName + ', ' + Driver.FirstName AS Driver    
FROM dbo.Trip_TripInformation
LEFT JOIN dbo.Trip_Location AS Origin ON Origin.RecordID = dbo.Trip_TripInformation.OriginLocationID    
LEFT JOIN dbo.Trip_TripDestinations ON dbo.Trip_TripInformation.RecordID = dbo.Trip_TripDestinations.TripID    
LEFT JOIN dbo.Trip_Location AS Destination ON Destination.RecordID = dbo.Trip_TripDestinations.LocationID    
LEFT JOIN dbo.Trip_TripDriverVehicle ON dbo.Trip_TripInformation.RecordID = dbo.Trip_TripDriverVehicle.TripID 
                                    AND dbo.Trip_TripDriverVehicle.DestinationID = dbo.Trip_TripDestinations.RecordID    
LEFT JOIN dbo.Vehicles ON dbo.Vehicles.RecordID = dbo.Trip_TripDriverVehicle.VehicleID    
LEFT JOIN dbo.Employees AS Driver ON dbo.Trip_TripDriverVehicle.DriverID = Driver.RecordID    
ORDER BY TripID

你需要把他们分组


快乐编码

您需要对它们进行分组


快乐编码

按起始日期排序
,然后选择
顶部(1)
按起始日期排序
,然后选择
顶部(1)

尝试在选择中添加行号,并将数据选择到临时表中(或使用CTE):


然后,您可以从RowNum=1的位置进行选择,尝试向select中添加行号,并将数据选择到临时表中(或使用CTE):


然后,您可以从RowNum=1中选择,您需要连接到一个派生表,该表提取每次旅行的唯一TripId和最早出发时间:

SELECT DISTINCT 
    ...
FROM dbo.Trip_TripInformation
INNER JOIN (
    SELECT TripID, MIN(OriginDepartureTime) as EarliestDepartureTime 
    FROM Trip_TripInformation 
    GROUP BY TripID
) EarliestTripOnly 
    ON 
        Trip_TripInformation.TripID = EarliestTripOnly.TripId
    AND 
       Trip_TripInformation.OriginDepartureTime 
           = EarliestTripOnly.EarliestDepartureTime
LEFT JOIN dbo.Trip_Location AS Origin ON Origin.RecordID =
    dbo.Trip_TripInformation.OriginLocationID    
    ...

您需要加入一个派生表,该表提取每次旅行的唯一TripId和最早出发时间:

SELECT DISTINCT 
    ...
FROM dbo.Trip_TripInformation
INNER JOIN (
    SELECT TripID, MIN(OriginDepartureTime) as EarliestDepartureTime 
    FROM Trip_TripInformation 
    GROUP BY TripID
) EarliestTripOnly 
    ON 
        Trip_TripInformation.TripID = EarliestTripOnly.TripId
    AND 
       Trip_TripInformation.OriginDepartureTime 
           = EarliestTripOnly.EarliestDepartureTime
LEFT JOIN dbo.Trip_Location AS Origin ON Origin.RecordID =
    dbo.Trip_TripInformation.OriginLocationID    
    ...

您可以使用Row_Number函数对每个TripID中的每个开始日期进行编号。此外,我将查询封装到一个公共表表达式中,这样我就可以只对那些行编号为1的行程进行fitler,如果行程返回多行,那么这些行程将表示最早的日期

With TripInfo As
    (
    Select TInfo.RecordID As TripID
        , TInfo.TripDate
        , Origin.LocationName As Origin
        , TInfo.OriginDepartureTime As StartDateTime
        , TInfo.OriginReturnTime As ReturnDateTime
        , Coalesce( TInfo.NoOfStudents, 0 ) As NumberOfStudents
        , Coalesce( TInfo.NoOfAdults, 0) As NumberOfAdults
        , Coalesce( TInfo.NoOfStudents, 0) + Coalesce(TInfo.NoOfAdults, 0) As NumberOfPassengers
        , Dest.LocationName As Destination
        , V.Vehicle
        , Driver.LastName + ', ' + Driver.FirstName As Driver    
        , Row_Number() Over ( Partition By TInfo.RecordId 
                              Order By TInfo.OriginDepartureTime ) As TripDateRnk
    From dbo.Trip_TripInformation As TInfo
        Left Join dbo.Trip_Location AS Origin 
            On Origin.RecordID = TInfo.OriginLocationID    
        Left Join dbo.Trip_TripDestinations As TDest
            On TInfo.RecordID = TDest.TripID    
        Left Join dbo.Trip_Location AS Destination 
            On Destination.RecordID = TDest.LocationID    
        Left Join dbo.Trip_TripDriverVehicle As TripV
            On TInfo.RecordID = TripV.TripID 
                And TripV.DestinationID = TDest.RecordID    
        Left Join dbo.Vehicles As V
            ON dbo.Vehicles.RecordID = TripV.VehicleID    
        Left Join dbo.Employees AS Driver 
            On dbo.Trip_TripDriverVehicle.DriverID = Driver.RecordID
    )
Select TripID, TripDate, Origin, StartDateTime, ReturnDateTime
    , NumberOfStudents, NumberOfAdults, NumberOfPassengers
    , Destination, Vehicle, Driver
From TripInfo
Where TripDateRank = 1  
Order By TripID
还有几个其他的观察结果:

  • 我注意到每个表都使用左连接。是否所有相关列都可以为空?例如,Trip_TripDriverHicle表中的VehicleID和DriverID列是否真的可以为空?您可以指定一辆没有车辆和没有驾驶员的行车驾驶员车辆

  • 我建议使用
    Coalesce
    函数,而不要使用命名错误的SQL Server特定函数
    IsNull
    。它们的操作基本相同,但Coalesce是标准的,允许两个以上的参数,而
    IsNull
    仅限于两个。它不会对您的代码或性能产生任何重大影响。这只是风格上的改进


  • 您可以使用Row_Number函数对每个TripID中的每个开始日期进行编号。此外,我将查询封装到一个公共表表达式中,这样我就可以只对那些行编号为1的行程进行fitler,如果行程返回多行,那么这些行程将表示最早的日期

    With TripInfo As
        (
        Select TInfo.RecordID As TripID
            , TInfo.TripDate
            , Origin.LocationName As Origin
            , TInfo.OriginDepartureTime As StartDateTime
            , TInfo.OriginReturnTime As ReturnDateTime
            , Coalesce( TInfo.NoOfStudents, 0 ) As NumberOfStudents
            , Coalesce( TInfo.NoOfAdults, 0) As NumberOfAdults
            , Coalesce( TInfo.NoOfStudents, 0) + Coalesce(TInfo.NoOfAdults, 0) As NumberOfPassengers
            , Dest.LocationName As Destination
            , V.Vehicle
            , Driver.LastName + ', ' + Driver.FirstName As Driver    
            , Row_Number() Over ( Partition By TInfo.RecordId 
                                  Order By TInfo.OriginDepartureTime ) As TripDateRnk
        From dbo.Trip_TripInformation As TInfo
            Left Join dbo.Trip_Location AS Origin 
                On Origin.RecordID = TInfo.OriginLocationID    
            Left Join dbo.Trip_TripDestinations As TDest
                On TInfo.RecordID = TDest.TripID    
            Left Join dbo.Trip_Location AS Destination 
                On Destination.RecordID = TDest.LocationID    
            Left Join dbo.Trip_TripDriverVehicle As TripV
                On TInfo.RecordID = TripV.TripID 
                    And TripV.DestinationID = TDest.RecordID    
            Left Join dbo.Vehicles As V
                ON dbo.Vehicles.RecordID = TripV.VehicleID    
            Left Join dbo.Employees AS Driver 
                On dbo.Trip_TripDriverVehicle.DriverID = Driver.RecordID
        )
    Select TripID, TripDate, Origin, StartDateTime, ReturnDateTime
        , NumberOfStudents, NumberOfAdults, NumberOfPassengers
        , Destination, Vehicle, Driver
    From TripInfo
    Where TripDateRank = 1  
    Order By TripID
    
    还有几个其他的观察结果:

  • 我注意到每个表都使用左连接。是否所有相关列都可以为空?例如,Trip_TripDriverHicle表中的VehicleID和DriverID列是否真的可以为空?您可以指定一辆没有车辆和没有驾驶员的行车驾驶员车辆

  • 我建议使用
    Coalesce
    函数,而不要使用命名错误的SQL Server特定函数
    IsNull
    。它们的操作基本相同,但Coalesce是标准的,允许两个以上的参数,而
    IsNull
    仅限于两个。它不会对您的代码或性能产生任何重大影响。这只是风格上的改进


  • 杜德。。。表别名将使其更具可读性。而且标签是正确的--“microsoft”太通用了欢迎使用堆栈溢出:DDuude。。。表别名将使其更具可读性。标记是正确的--“microsoft”太通用了欢迎使用堆栈溢出:在这个实例中,
    COALESCE
    不会有任何区别。@Martin-不过,看看链接,这可能可以通过重写查询来处理,将子查询放入派生表中,然后使用COALESCE。合并转化为案例。。When是SQL spec的一部分。@Martin-值得注意的是,Connect文章是三年前的。我想知道它是在R2中寻址还是将在Denali中寻址。在这个例子中,
    COALESCE
    不会有任何区别。@Martin-不过,看看链接,这可能可以通过重写查询来处理,将子查询放入派生表中,然后使用COALESCE。合并转化为案例。。When是SQL spec的一部分。@Martin-值得注意的是,Connect文章是三年前的。我不知道它是用R2写的还是会用Denali写的。我认为这行不通。我可能有一次带多个目的地的实地考察。这将为该TripID生成两行(即TripID 1)。这可能会发生在另一次旅行中(假设TripID=2),我不想只返回整个表的顶部,而是按DestinationArrivalDateTime的顺序返回每个TripID的顶部。我认为这行不通。我可能有一次带多个目的地的实地考察。这将为该TripID生成两行(即TripID 1)。这可能会发生在另一次旅行中(假设TripID=2),我不想只返回整个表的顶部,而是返回DestinationArrivalDateTime命令的每个TripID的顶部。