如何在SQL Server中基于行值获取列名?

时间:2018-12-05 21:21:12

标签: sql sql-server

我有一个查找表,其中包含很多带有值的列。 如何使用Microsoft SQL Server从输入中获得此输出?
(基本上选择列名称,其中Date = MAX()AND ColX到ColZ值= <5.4)。

  1. 找到具有最新日期的行。 MAX(日期)
  2. 查看ColX,我的值比ColX的值高5.4吗?是/否
  3. 是的。在ColY中看,我的价值比ColY的价值高5.4吗?是/否
  4. 是的。在ColZ中看,我的值比ColZ的值高5.4吗?是/否
  5. 不。输出列ColY AS列

输入

ID        Date                 ColX    ColY    ColZ
-----------------------------------------------------
79185673  2018-11-28 00:00:00     3       5       7
79185673  2018-12-02 00:00:00     2       4       6
79185673  2018-12-04 00:00:00     4       5       6

输出

ID        Date                 Column 
--------------------------------------
79185673  2018-12-04 00:00:00    ColY

4 个答案:

答案 0 :(得分:2)

这是您要找的吗

SELECT TOP 1 *,
       CASE WHEN 5.4  > ColZ 
            THEN 'ColZ'
            WHEN 5.4 > ColY
            THEN 'ColY'
            WHEN 5.4 > ColX
            THEN 'ColX'
      END [Column]
FROM T
ORDER BY [Date] DESC;

答案 1 :(得分:1)

DDL中固定的列名称是元数据,而不是存储在行中的值。您可以通过从information_schema.columns表中进行选择来查看列名。但是,要实现所请求的逻辑,可以使用SQL中的CASE语句来完成。

假设表名称为table1,请尝试以下查询:

declare @compare_value decimal(2,1);
@compare_value = 5.4;

select
t.ID,
t.Date,
case when t.colX <= @compare_value then
    case when t.ColY <= @compare_value then
        case when t.ColZ <= @compare_value then
            'ColZ'
        else
            'ColY'
        end
    else
        'ColY'
    end
else
    'ColX'
end as "Column"
from table1 t
where t.date = (
                   select max(t1.date)
                   from table1 t1
                   where t1.ID = t.ID
               );

请注意:该代码已嵌套到3个级别,并且需要为每列嵌套(可能是150个级别)!那是一些严重的spagetti代码。它会工作,但看起来会很混乱。如果数据量巨大,那么性能也可能成为问题,因为SQL对于复杂的逻辑并不是真正有用。 使用SQL仅选择所需的数据并使用存储过程,或者通过ODBC连接(例如.NET或Python)提供数据,会更好。然后在那里进行复杂的逻辑处理。

答案 2 :(得分:1)

我们将逐步进行。这是数据设置:

DECLARE @table TABLE
  (
    ID   INTEGER  NOT NULL
   ,Date DATETIME NOT NULL
   ,ColX INTEGER  NOT NULL
   ,ColY INTEGER  NOT NULL
   ,ColZ INTEGER  NOT NULL
  );
INSERT INTO @table
  (ID,Date,ColX,ColY,ColZ)
VALUES
  (79185673, '2018-11-28T00:00:00', 3, 5, 7);
INSERT INTO @table
  (ID,Date,ColX,ColY,ColZ)
VALUES
  (79185673, '2018-12-02T00:00:00', 2, 4, 6);
INSERT INTO @table
  (ID,Date,ColX,ColY,ColZ)
VALUES
  (79185673, '2018-12-04T00:00:00', 4, 5, 6);

首先,我们将找到具有最大日期的记录。

SELECT TOP (1)
  *
FROM
  @table
ORDER BY
  [Date] DESC

+----------+-------------------------+------+------+------+
|    ID    |          Date           | ColX | ColY | ColZ |
+----------+-------------------------+------+------+------+
| 79185673 | 2018-12-04 00:00:00.000 |    4 |    5 |    6 |
+----------+-------------------------+------+------+------+

因此形成了我们的基本数据集。从这里开始,我们要UNPIVOT将所有列值都放入一个列中。您必须在UNPIVOT中键入所有其他列名称,但是您可能只需使用默认的SELECT TOP N ROWS查询并复制和粘贴,就可以让SSMS为您执行一些脚本编制工作从那里开始的列名。

SELECT
  *
FROM
  (
    SELECT
      TOP (1)
      *
    FROM
      @table
    ORDER BY
      [Date] DESC
  ) AS d
  UNPIVOT
  (
    Nums
    FOR ColName IN (ColX, ColY, ColZ)
  ) AS p

+----------+-------------------------+------+---------+
|    ID    |          Date           | Nums | ColName |
+----------+-------------------------+------+---------+
| 79185673 | 2018-12-04 00:00:00.000 |    4 | ColX    |
| 79185673 | 2018-12-04 00:00:00.000 |    5 | ColY    |
| 79185673 | 2018-12-04 00:00:00.000 |    6 | ColZ    |
+----------+-------------------------+------+---------+

根据注释,列中的数字始终在增加,因此我们可以安全地对其进行排序并保持原始顺序。但是我们只关心小于目标数(在此示例中为5.4)的数字。这就是我们的WHERE子句。而且我们希望最大的数字小于5.4,因此我们将使用降序ORDER BY子句。我们只需要一个值,因此最终结果中只需要TOP (1)

DECLARE @target DECIMAL(5,1) = 5.4;

SELECT TOP (1)
  *
FROM
  (
    SELECT
      TOP (1)
      *
    FROM
      @table
    ORDER BY
      [Date] DESC
  ) AS d
  UNPIVOT
  (
    Nums
    FOR ColName IN (ColX, ColY, ColZ)
  ) AS p
WHERE
  p.Nums < @target
ORDER BY
  p.Nums DESC;

+----------+-------------------------+------+---------+
|    ID    |          Date           | Nums | ColName |
+----------+-------------------------+------+---------+
| 79185673 | 2018-12-04 00:00:00.000 |    5 | ColY    |
+----------+-------------------------+------+---------+

答案 3 :(得分:0)

根据他们的评论,我怀疑这就是OP的要求,但根据他们的问题,答案是:

DECLARE @MyValue decimal(2,1) = 5.4

WITH CTE AS(
    SELECT ID,
           [Date],
           ColX,Coly,ColZ,
           ROW_NUMBER() OVER (ORDER BY [Date] DESC) AS RN --PARTITION BY ID?
    FROM TheirTable)
SELECT ID,
       [Date],
       CASE WHEN ColZ < @MyValue THEN 'ColZ'
            WHEN ColY < @MyValue THEN 'ColY'
            WHEN ColX < @MyValue THEN 'ColX'
       END AS [Column]
FROM CTE
WHERE RN = 1;

他们的评论(在问题下)有点领先,但是在更新他们的问题后,他们仍然只有5列,所以我要假设他们实际上有5列。也没有真正的确切解释,这是我的“最佳猜测”。