需要询问SQL Server中的关联查询

时间:2013-02-26 20:18:49

标签: sql sql-server

我有2个查询,第一个:

SELECT XEDETALLES_BODEGA.IDPROD, SUM(XEDETALLES_BODEGA.CANTIDAD) AS CANTIDAD, MONTH (XEDETALLES_BODEGA.XFECHA) AS MES, 
       YEAR(XEDETALLES_BODEGA.XFECHA) AS ANO, CONVERT(DATETIME, CAST(YEAR(XEDETALLES_BODEGA.XFECHA) AS VARCHAR) + '-1-' + CAST(MONTH(XEDETALLES_BODEGA.XFECHA) AS VARCHAR) , 103)   AS FECHA
FROM XEDETALLES_BODEGA 
    LEFT OUTER JOIN xEGRESOS_BODEGA ON XEDETALLES_BODEGA.IDEGRESO = xEGRESOS_BODEGA.ID
WHERE (YEAR(XEDETALLES_BODEGA.XFECHA) = 2012) AND XEDETALLES_BODEGA.IDPROD <> 0
GROUP BY XEDETALLES_BODEGA.IDPROD, MONTH(XEDETALLES_BODEGA.XFECHA), YEAR(XEDETALLES_BODEGA.XFECHA)
ORDER BY XEDETALLES_BODEGA.IDPROD, MONTH(XEDETALLES_BODEGA.XFECHA), YEAR(XEDETALLES_BODEGA.XFECHA)

和第二个查询:

SELECT TOP (1) PRECIOUNIT
FROM XCDETALLES_BODEGA
WHERE (IDPROD = FIRSTQUERY.IDPROD) AND (XFECHA < FIRSTQUERY.FECHA)
ORDER BY XFECHA DESC

问题:对于第一个表上的每个记录,我需要在替换IDPROD和FECHA后获得第二个查询产生的PRECIOUNIT。

2 个答案:

答案 0 :(得分:1)

如果您希望第二个查询返回的列添加到第一个查询的结果,您可以简单地将第二个查询作为相关子查询结合到第一个查询中/ em>,像这样:

SELECT
  XEDETALLES_BODEGA.IDPROD,
  SUM(XEDETALLES_BODEGA.CANTIDAD) AS CANTIDAD,
  MONTH(XEDETALLES_BODEGA.XFECHA) AS MES, 
  YEAR(XEDETALLES_BODEGA.XFECHA) AS ANO,
  CONVERT(DATETIME, CAST(YEAR(XEDETALLES_BODEGA.XFECHA) AS VARCHAR) + '-1-' + CAST(MONTH(XEDETALLES_BODEGA.XFECHA) AS VARCHAR), 103) AS FECHA,
  (
    SELECT TOP (1) PRECIOUNIT
    FROM XCDETALLES_BODEGA
    WHERE (XCDETALLES_BODEGA.IDPROD = XEDETALLES_BODEGA.IDPROD)
      AND (XCDETALLES_BODEGA.XFECHA < CONVERT(DATETIME, CAST(YEAR(XEDETALLES_BODEGA.XFECHA) AS VARCHAR) + '-1-' + CAST(MONTH(XEDETALLES_BODEGA.XFECHA) AS VARCHAR), 103))
    ORDER BY XCDETALLES_BODEGA.XFECHA DESC
  ) AS PRECIOUNIT
FROM XEDETALLES_BODEGA 
  LEFT OUTER JOIN xEGRESOS_BODEGA ON XEDETALLES_BODEGA.IDEGRESO = xEGRESOS_BODEGA.ID
WHERE (YEAR(XEDETALLES_BODEGA.XFECHA) = 2012) AND XEDETALLES_BODEGA.IDPROD <> 0
GROUP BY XEDETALLES_BODEGA.IDPROD, MONTH(XEDETALLES_BODEGA.XFECHA), YEAR(XEDETALLES_BODEGA.XFECHA)
ORDER BY XEDETALLES_BODEGA.IDPROD, MONTH(XEDETALLES_BODEGA.XFECHA), YEAR(XEDETALLES_BODEGA.XFECHA)
;

但是,此查询还有改进的余地。

首先,我将介绍更短的表别名。请考虑重写:

SELECT
  xed.IDPROD,
  SUM(xed.CANTIDAD) AS CANTIDAD,
  MONTH(xed.XFECHA) AS MES, 
  YEAR(xed.XFECHA) AS ANO,
  CONVERT(DATETIME, CAST(YEAR(xed.XFECHA) AS VARCHAR) + '-1-' + CAST(MONTH(xed.XFECHA) AS VARCHAR), 103) AS FECHA,
  (
    SELECT TOP (1) PRECIOUNIT
    FROM XCDETALLES_BODEGA AS xcd
    WHERE (xcd.IDPROD = xed.IDPROD)
      AND (xcd.XFECHA < CONVERT(DATETIME, CAST(YEAR(xed.XFECHA) AS VARCHAR) + '-1-' + CAST(MONTH(xed.XFECHA) AS VARCHAR), 103))
    ORDER BY xcdXFECHA DESC
  ) AS PRECIOUNIT
FROM XEDETALLES_BODEGA AS xed
  LEFT OUTER JOIN xEGRESOS_BODEGA AS xeg ON xed.IDEGRESO = xeg.ID
WHERE (YEAR(xed.XFECHA) = 2012) AND xed.IDPROD <> 0
GROUP BY xed.IDPROD, MONTH(xed.XFECHA), YEAR(xed.XFECHA)
ORDER BY xed.IDPROD, MONTH(xed.XFECHA), YEAR(xed.XFECHA)
;

您是否同意较短的别名会使您的查询更具可读性?

另一个问题是您的分组标准,特别是这两项:

MONTH(xed.XFECHA), YEAR(xed.XFECHA)

他们当然会明确您的意图,但是当您将它们重新设置为datetime值时,它们也会进行多次转换。现在我们也在相关子查询中使用相同的datetime。这些转换绝对没有必要,因为你可以反过来谈论它。您可以将日期时间“舍入”到相应月份的开头,而不是从datetime中提取年份和月份,然后将其转换回datetime。以下表达式就是这样:

DATEADD(MONTH, DATEDIFF(MONTH, 0, xed.XFECHA), 0)

现在,当您需要将月份和年份显示为数值时,您可以从上面的表达式中提取它们,因为这样可以提供相同的月份和年份。所以,现在看看你的查询:

SELECT
  xed.IDPROD,
  SUM(xed.CANTIDAD) AS CANTIDAD,
  MONTH(DATEADD(MONTH, DATEDIFF(MONTH, 0, xed.XFECHA), 0)) AS MES, 
  YEAR(DATEADD(MONTH, DATEDIFF(MONTH, 0, xed.XFECHA), 0)) AS ANO,
  DATEADD(MONTH, DATEDIFF(MONTH, 0, xed.XFECHA), 0) AS FECHA,
  (
    SELECT TOP (1) PRECIOUNIT
    FROM XCDETALLES_BODEGA AS xcd
    WHERE (xcd.IDPROD = xed.IDPROD)
      AND (xcd.XFECHA < DATEADD(MONTH, DATEDIFF(MONTH, 0, xed.XFECHA), 0))
    ORDER BY xcd.XFECHA DESC
  ) AS PRECIOUNIT
FROM XEDETALLES_BODEGA AS xed
  LEFT OUTER JOIN xEGRESOS_BODEGA AS xeg ON xed.IDEGRESO = xeg.ID
WHERE (YEAR(xed.XFECHA) = 2012) AND xed.IDPROD <> 0
GROUP BY xed.IDPROD, DATEADD(MONTH, DATEDIFF(MONTH, 0, xed.XFECHA), 0)
ORDER BY xed.IDPROD, DATEADD(MONTH, DATEDIFF(MONTH, 0, xed.XFECHA), 0)
;

我知道你在想什么。新表达式似乎使用了太多次,这不会使查询看起来非常干净,甚至更少,因为表达式不是那么短。这有帮助吗?是的,它可以。您可以使用派生表。将连接,WHERE过滤器和必要的列(包括计算月初的表达式)放入子选择中,让主查询从中提取数据。将分组,排序和相关子查询保留在主SELECT中。简而言之,这就是你可能最终得到的结果:

SELECT
  s.IDPROD,
  SUM(s.CANTIDAD) AS CANTIDAD,
  MONTH(s.XFECHA) AS MES, 
  YEAR(s.XFECHA) AS ANO,
  s.XFECHA,
  (
    SELECT TOP (1) PRECIOUNIT
    FROM XCDETALLES_BODEGA AS xcd
    WHERE (xcd.IDPROD = xed.IDPROD)
      AND (xcd.XFECHA < s.XFECHA
    ORDER BY xcd.XFECHA DESC
  ) AS PRECIOUNIT
FROM (
  SELECT
    xed.IDPROD,
    xed.CANTIDAD,
    DATEADD(MONTH, DATEDIFF(MONTH, 0, xed.XFECHA), 0) AS FECHA
  FROM XEDETALLES_BODEGA AS xed
    LEFT OUTER JOIN xEGRESOS_BODEGA AS xeg ON xed.IDEGRESO = xeg.ID
  WHERE (YEAR(xed.XFECHA) = 2012) AND xed.IDPROD <> 0
) s
GROUP BY s.IDPROD, s.XFECHA
ORDER BY s.IDPROD, s.XFECHA
;

但是,如果您使用的是SQL Server 2005或更高版本,则不需要派生表 - 您可以使用CROSS APPLY。这里:

SELECT
  xed.IDPROD
  SUM(xed.CANTIDAD) AS CANTIDAD,
  MONTH(s.XFECHA) AS MES, 
  YEAR(s.XFECHA) AS ANO,
  s.XFECHA,
  (
    SELECT TOP (1) PRECIOUNIT
    FROM XCDETALLES_BODEGA AS xcd
    WHERE (xcd.IDPROD = xed.IDPROD)
      AND (xcd.XFECHA < s.XFECHA
    ORDER BY xcd.XFECHA DESC
  ) AS PRECIOUNIT
FROM XEDETALLES_BODEGA AS xed
  LEFT OUTER JOIN xEGRESOS_BODEGA AS xeg ON xed.IDEGRESO = xeg.ID
CROSS APPLY (
  SELECT DATEADD(MONTH, DATEDIFF(MONTH, 0, xed.XFECHA), 0) AS FECHA
) AS s
WHERE (YEAR(xed.XFECHA) = 2012) AND xed.IDPROD <> 0
GROUP BY xed.IDPROD, s.XFECHA
ORDER BY xed.IDPROD, s.XFECHA
;

答案 1 :(得分:0)

您可以将第一个查询放入CTE并将其加入第二个查询。

;WITH 
 FIRSTQUERY (IDPROD, CANTIDAD, MES, ANO, FECHA)
 AS
 (
  SELECT XEDETALLES_BODEGA.IDPROD
    ,SUM(XEDETALLES_BODEGA.CANTIDAD) AS CANTIDAD
    ,MONTH(XEDETALLES_BODEGA.XFECHA) AS MES
    ,YEAR(XEDETALLES_BODEGA.XFECHA) AS ANO
    ,CONVERT(DATETIME, CAST(YEAR(XEDETALLES_BODEGA.XFECHA) AS VARCHAR) + '-1-' + CAST(MONTH(XEDETALLES_BODEGA.XFECHA) AS VARCHAR) , 103) AS FECHA
  FROM XEDETALLES_BODEGA 
  LEFT OUTER JOIN xEGRESOS_BODEGA ON XEDETALLES_BODEGA.IDEGRESO = xEGRESOS_BODEGA.ID
  WHERE (YEAR(XEDETALLES_BODEGA.XFECHA) = 2012) AND XEDETALLES_BODEGA.IDPROD <> 0
  GROUP BY XEDETALLES_BODEGA.IDPROD, MONTH(XEDETALLES_BODEGA.XFECHA),     YEAR(XEDETALLES_BODEGA.XFECHA)
  ORDER BY XEDETALLES_BODEGA.IDPROD, MONTH(XEDETALLES_BODEGA.XFECHA), YEAR(XEDETALLES_BODEGA.XFECHA)
 )
SELECT TOP (1) XCDETALLES_BODEGA.PRECIOUNIT
FROM XCDETALLES_BODEGA
LEFT JOIN FIRSTQUERY on FIRSTQUERY.IDPROD = XCDETALLES_BODEGA.IDPROD
WHERE XCDETALLES_BODEGA.XFECHA < FIRSTQUERY.FECHA
ORDER BY XFECHA DESC