【问题标题】:SELECT Only one value per ID - SQL Server [closed]每个 ID 仅选择一个值 - SQL Server [关闭]
【发布时间】:2020-01-26 01:03:06
【问题描述】:

我不知道这是否可能,我什至不知道如何用谷歌搜索它。

我使用的是 SQL Server 2014。

现在我有一个 SP,它输出一个表格,其中包含 MovementID、Vehicle、VehicleType 和 Total of sales 的数据。 但它会将所有车辆 ID 和车辆类型归为一个总数。

我需要分开车辆,但我可以。 问题是总数出现重复或重复等。

那么,我怎样才能每个 ID 只选择一次该列(在示例中为移动 ID)。

当然,我会继续努力,但我们将不胜感激。

[

【问题讨论】:

  • 我们想看看您正在使用的代码。在发布输入/输出/所需输出时,文本优先于图像。
  • 这是一个前端解决方案,而不是 SQL Server 解决方案。可以得到你要求的东西,但绝对不推荐。
  • 您面临的最大问题是您没有正确规范化的数据。您的列中有分隔列表。这违反了 1NF 并导致令人难以置信的痛苦。
  • Sean Lange - 这是生产中系统的数据库,所以我对此无能为力。我必须解决已经存在的问题。
  • 你为什么假设 F 与 V01 一起使用而 T 与 V02 一起使用?我会假设 VehicleTypes 中值的顺序实际上是独立于 VehicleID 进行排序的。更重要的是,您有一个存储过程,它已经生成了您需要的信息 - 所以使用该逻辑创建一个新的并按照您想要的方式格式化结果。

标签: sql sql-server sorting querying


【解决方案1】:

您可以按如下方式拆分和加入:

;with cte_vids as (
    select * from #tblmove
    cross apply udf_split( vehicleids, ',') 
), cte_vtypes as (
    select * from #tblmove
    cross apply udf_split( vehicletypes, ',')
)
select cid.movementid, cid.[value] as vehichleid, cty.[value] as vehicletype, 
    case when cid.rown = 1 then cid.total else null end as total 
from cte_vids cid join cte_vtypes cty
on cid.movementid = cty.movementid and cid.vehicleids = cty.vehicleids and cid.rown = cty.rown

答案如下:

+------------+------------+-------------+------ -+
|运动ID |车辆 |车型 |总计|
+------------+------------+--------------+--------+
| 1 | V01 | F | 200 |
| 1 | V02 | T |空 |
| 2 | V04 |五 | 140 |
| 3 | V03 | F | 300 |
| 3 | V02 | F |空 |
+------------+------------+--------------+--------+

我使用了一个你可以创建如下的函数:

CREATE Function dbo.udf_split( @str varchar(max), @delimiter as varchar(5) )
RETURNS @retTable Table 
( RowN int,
value varchar(max)
)
AS
BEGIN
DECLARE @xml as xml
    SET @xml = cast(('<X>'+replace(@str,@delimiter ,'</X><X>')+'</X>') as xml)
    INSERT INTO @retTable   
    SELECT RowN = Row_Number() over (order by (SELECT NULL)), N.value('.', 'varchar(MAX)') as value FROM @xml.nodes('X') as T(N)
RETURN
END

【讨论】:

  • 我想你给了他他已经拥有的东西。
  • :) 只是注意到可用性问题,我看到了第一个结果集并提供了解决方案
  • 修复了这个问题。
【解决方案2】:

您可以按 MovementID 对记录进行分区,然后为分区中的每一行生成一个ROW_NUMBER。然后,您可以使用IIF 仅在行号为 1 时显示总计,即该行是分区中的第一行。它应该看起来像这样:

SELECT MovementID, VehicleID, VehicleType, IIF(rowno = 1, Total, NULL)
FROM
(
    SELECT MovementID, VehicleID, VehicleType, Total, 
     ROW_NUMBER() OVER (PARTITION BY MovementID ORDER BY MovementID) AS rowno
    FROM <WHAT_I_HAVE_NOW>
) tmp

【讨论】:

  • 这对性能有何影响?谢谢你的回答!
  • 我不希望对性能产生非常明显的影响,因为分区发生在 MovementID 上,我认为它是表上的主键?如果是这样,它将被索引,因此无需费力地扫描行即可完成分区。
【解决方案3】:

您可以使用LAG 查看上一行的值。

with q as (your query here)
select 
  movementid, vehicleid, vecicletype, 
  case when movementid = lag(movementid) over (order by movementid, vehicleid)
    then null else total
  end as total
from q
order by movementid, vehicleid;

【讨论】:

  • 嗯,这行得通!,到目前为止。很抱歉问同样的问题,但这对性能有多大影响?非常感谢,我会尝试所有的解决方案,以便我可以根据性能使用最好的解决方案,但是,这行得通
  • 嗯,这需要它需要的时间。在谈论如此糟糕的数据库时,询问性能影响似乎有点奇怪。我不认为像LAG 那样扫描结果行会非常昂贵。但是,只需测量它。多次运行您的查询,多次运行我的查询,查看运行时间。毕竟,我真的不建议使用我的查询。这是应该在 GUI 层中完成的事情。在那里,将循环读取所选数据以填充网格。很容易在该循环中检测到相同的移动ID,并且不会填充整个单元格。
  • 每个人似乎都认为我可以控制数据库结构,我可以更改 GUI,他们可以在其他地方找到一种方法。我不。我什么都做不了。这就是为什么我首先以这种方式问这个问题。谢谢,性能没有受到明显影响,所以它可以工作。
  • 是的,通常一个人可以控制数据库或 GUI 或两者。因此,我们正在努力提供最好的建议,这不是与查询作斗争,而是做应该做的事情。您的数据库和 GUI 是第三方的,并且您正在更改第三方 GUI 再次使用的存储过程,这是一种非常特殊的情况。在 Oracle 中,可以用视图替换原始表并使用而不是触发器来编写规范化表,但我不认为 SQL Server 具有此功能。所以,这里的查询已经是最好的解决方案了。很高兴,它对你有用。
猜你喜欢
  • 1970-01-01
  • 2013-07-21
  • 1970-01-01
  • 1970-01-01
  • 2012-04-03
  • 2013-05-06
  • 2019-10-03
  • 2020-09-09
  • 2019-12-17
相关资源
最近更新 更多