-4

我有 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。

4

2 回答 2

1

如果您希望将第二个查询返回的列添加到第一个查询的结果中,您可以简单地将第二个查询作为相关子查询合并到第一个查询中,如下所示:

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相关子查询中使用相同的方法。这些转换是绝对不必要的,因为你可以反过来做。您可以将日期时间“向下舍入”到相应月份的开始,而不是从 a 中提取年份和月份datetime,然后将它们转换回 a 。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
;
于 2013-02-26T22:20:17.670 回答
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
于 2013-02-26T21:41:48.897 回答