【问题标题】:Is there a faster way to find the order of a COLUMN?有没有更快的方法来查找 COLUMN 的顺序?
【发布时间】:2022-01-27 10:37:57
【问题描述】:

我的 SQL Server 表是这样的

ID   a_Toyota   a_Mazda   a_Nissan   a_Kia   a_Honda   a_Subaru     SoldCar   CarOrder
1    8000       7000      6200       8500    6500      7000         Mazda     NULL
2    4000       5000      4500       3500    3500      5000         Mazda     NULL
3    5400       5000      4500       5500    5500      4600         Mazda     NULL
4    5600       6300      7500       8200    6500      7300         Mazda     NULL
5    8500       7400      7400       6500    9500      9000         Mazda     NULL
6    9900       8000      9900       7300    8100      8000         Mazda     NULL

我想更新 CarOrder 字段,因此它具有已售汽车价格与其他汽车价格相比的顺序。

所以对于 ID 1 的汽车价格订购为 a_Kia (8500)1sta_Toyota (8000)2nd strong>和a_Mazda & a_Subaru (7000)第3a_Honda (6500)第5 a_Nissan (6200) 第六 卖的车是马自达,排在第三位,所以表格如下

ID   a_Toyota   a_Mazda   a_Nissan   a_Kia   a_Honda   a_Subaru     SoldCar   CarOrder
1    8000       7000      6200       8500    6500      7000         Mazda     3
2    4000       5000      4500       3500    3500      5000         Subaru    1
3    5400       5000      4500       5500    5500      4600         Toyota    3
4    5600       6300      7500       8200    6500      7300         Honda     4
5    8500       7400      7400       6500    9500      9000         Honda     1
6    9900       8000      9900       7300    8100      8000         Honda     3

我可以找到带有大 CASE 语句的订单

UPDATE mytable
SET CarOrder =
CASE WHEN SoldCar = 'Toyota' AND a_Toyota>=a_Mazda AND a_Toyota>=a_Nissan AND ... AND a_Toyota>=a_Subaru THEN 1 
CASE WHEN SoldCar = 'Toyota' AND a_Toyota<a_Mazda AND a_Toyota>=a_Nissan AND ... AND a_Toyota>=a_Subaru THEN 2 
CASE WHEN SoldCar = 'Toyota' AND a_Toyota>=a_Mazda AND a_Toyota<a_Nissan AND ... AND a_Toyota>=a_Subaru THEN 2 
.
.
.
CASE WHEN SoldCar = 'Toyota' AND a_Toyota>=a_Mazda AND a_Toyota>=a_Nissan AND ... AND a_Toyota<a_Subaru THEN 2 
.
.
.
CASE WHEN SoldCar = 'Toyota' AND a_Toyota<a_Mazda AND a_Toyota<a_Nissan AND ... AND a_Toyota>=a_Subaru THEN 3
..
..
..

但这将是一个巨大的案例陈述。

我想知道是否有人有更简单的方法来做到这一点?

【问题讨论】:

  • 仅供参考 case 是一个表达式而不是一个语句
  • @DaleK 感谢您的澄清
  • 老实说,您的设计似乎是非规范化的。我怀疑你应该有 2 个表,而不是 1 个,并且每个 ID 应该有很多行(每个制作 1 个)。
  • 我不太符合你的逻辑,但你也许可以使用row_number() 窗口函数来进行订购。
  • 您的表严重非规范化。我建议您在继续之前先整理好设计,否则您将在每次查询时都束手无策

标签: sql sql-server tsql


【解决方案1】:

另一种基于 XQuery 的方法。

对于 ID=2 的行,Subary 和 Mazda 之间存在平局。它们的价值都是 5000。

SQL

-- DDL and sample data population, start
DECLARE @tbl TABLE (
    ID INT IDENTITY(1,1) PRIMARY KEY, 
    a_Toyota INT,
    a_Mazda INT,
    a_Nissan INT,
    a_Kia INT,
    a_Honda INT,
    a_Subaru INT,
    SoldCar VARCHAR(20)
);
INSERT INTO @tbl
(
    a_Toyota,
    a_Mazda,
    a_Nissan,
    a_Kia,
    a_Honda,
    a_Subaru,
    SoldCar
) VALUES
(8000, 7000, 6200, 8500, 6500, 7000, 'Mazda'),
(4000, 5000, 4500, 3500, 3500, 5000, 'Subaru'),
(5400, 5000, 4500, 5500, 5500, 4600, 'Toyota'),
(5600, 6300, 7500, 8200, 6500, 7300, 'Honda'),
(8500, 7400, 7400, 6500, 9500, 9000, 'Honda'),
(9900, 8000, 9900, 7300, 8100, 8000, 'Honda');
-- DDL and sample data population, end

SELECT t.*, CarOrder 
FROM @tbl AS t
    CROSS APPLY (SELECT a_Toyota, a_Mazda, a_Nissan,
    a_Kia, a_Honda, a_Subaru, SoldCar
    FOR XML PATH(''), TYPE, ROOT('root')) AS t1(c)
CROSS APPLY (SELECT c.query('<root>
{
for $r in /root/*
order by data($r) descending
return <r>
        <make>{local-name($r)}</make>
        <salePrice>{data($r)}</salePrice>
    </r>
}
</root>').query('
    let $soldcar := sql:column("SoldCar")
    for $r in /root/r[contains((make/text())[1], $soldcar)]
    let $pos := count(root/*[. << $r])
    return $pos').value('.','INT')
) AS t2(CarOrder);

输出

+----+----------+---------+----------+-------+---------+----------+---------+----------+
| ID | a_Toyota | a_Mazda | a_Nissan | a_Kia | a_Honda | a_Subaru | SoldCar | CarOrder |
+----+----------+---------+----------+-------+---------+----------+---------+----------+
|  1 |     8000 |    7000 |     6200 |  8500 |    6500 |     7000 | Mazda   |        3 |
|  2 |     4000 |    5000 |     4500 |  3500 |    3500 |     5000 | Subaru  |        2 |
|  3 |     5400 |    5000 |     4500 |  5500 |    5500 |     4600 | Toyota  |        3 |
|  4 |     5600 |    6300 |     7500 |  8200 |    6500 |     7300 | Honda   |        4 |
|  5 |     8500 |    7400 |     7400 |  6500 |    9500 |     9000 | Honda   |        1 |
|  6 |     9900 |    8000 |     9900 |  7300 |    8100 |     8000 | Honda   |        3 |
+----+----------+---------+----------+-------+---------+----------+---------+----------+

【讨论】:

    【解决方案2】:

    这是一个选项,您不必枚举要取消透视的列。

    这也假设列名的前缀为a_

    示例或dbFiddle

    with cte as (
    Select * 
     From  YourTable A
     Cross Apply ( 
                   Select *
                         ,rn=row_number() over (order by convert(decimal(12,2),value) desc)
                     From OpenJson((Select A.* For JSON Path,Without_Array_Wrapper  )) 
                     Where [Key] not in ('ID','SoldCar','CarOrder')
                 ) B
     Where [key] ='a_'+SoldCar  collate SQL_Latin1_General_CP1_CI_AS 
    )
    Update cte set CarOrder = RN
    

    更新后的表格

    【讨论】:

    • 您可以右键单击结果集的一角并使用标题进行复制。稍后,在此处转换为 github 风格的标记表:ozh.github.io/ascii-tables 并粘贴到此处。我们可以避免使用图片。
    【解决方案3】:

    假设表结构类似如下:

    CREATE TABLE tempdb..cars
    (
        ID INT NOT NULL,
        a_Toyota INT NOT NULL,
        a_Mazda INT NOT NULL,
        a_Nissan INT NOT NULL,
        a_Kia INT NOT NULL,
        a_Honda INT NOT NULL,
        a_Subaru INT NOT NULL,
        SoldCar VARCHAR(100) NOT NULL,
        CarOrder INT NULL
    );
    

    一种方法是利用APPLY operator。假设使用 CROSS APPLY 和非密集 RANK(相对于 DENSE RANK)以及降序来确定您的排序,类似下面的内容应该为您提供来自上述表结构的结果集:

    SELECT  c.ID, c.SoldCar, o.ord AS CarOrder
    FROM    tempdb..cars c
    CROSS APPLY
            (
            SELECT  t.ord
            FROM    (
                    SELECT  r.car, RANK() OVER (ORDER BY r.qty DESC) AS ord
                    FROM    (
                            SELECT  c.a_Toyota AS qty, 'Toyota' AS car
                            UNION ALL
                            SELECT  c.a_Mazda AS qty, 'Mazda' AS car
                            UNION ALL
                            SELECT  c.a_Nissan AS qty, 'Nissan' AS car
                            UNION ALL
                            SELECT  c.a_Kia AS qty, 'Kia' AS car
                            UNION ALL
                            SELECT  c.a_Honda AS qty, 'Honda' AS car
                            UNION ALL
                            SELECT  c.a_Subaru AS qty, 'Subaru' AS car
                            ) r
                    ) t
            WHERE   t.car = c.SoldCar
            ) o
    

    【讨论】:

      【解决方案4】:

      表格结构需要从一开始就转变为应有的形式,以便轻松确定正确的顺序并满足任意数量的品牌。

      您可以使用 cross-apply 和 row_number 将每个值与其序号位置匹配,使用可更新的 CTE 来更新基表。

      with cars as (
          select * from t
          cross apply (
              select case soldcar
                when 'Toyota' then a_Toyota
                when 'Mazda'  then a_Mazda
                when 'Nissan' then a_Nissan
                when 'Honda'  then a_Honda
                when 'Subaru' then a_Subaru
              end
          )s(SoldValue)
          cross apply (
            select rank() over (order by v desc) co, v
            from (values(a_toyota),(a_mazda),(a_nissan),(a_kia),(a_honda),(a_subaru))v(v)
          )c
          where SoldValue=v
      )
      update cars set carOrder=co
      

      Demo Fiddle

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 2021-10-14
        • 1970-01-01
        • 2018-06-24
        • 2015-01-07
        • 1970-01-01
        • 2015-11-29
        • 2020-01-16
        • 1970-01-01
        相关资源
        最近更新 更多