5

我有一个包含两列的表:intGroupID、decAmount

如果对于每个正 (+) decAmount,有一个相等和相反的负 (-) decAmount,我想要一个基本上可以返回 intGroupID 作为结果的查询。

因此 (id=1,amount=1.0),(1,2.0),(1,-1.0),(1,-2.0) 的表将返回 1 的 intGroupID,因为对于每个正数都存在一个负数要匹配的号码。

到目前为止我所知道的是必须有相等数量的 decAmount(所以我强制执行 count(*) % 2 = 0)并且所有行的总和必须 = 0.0。但是,通过该逻辑得到的一些情况是:

身份证 | 数量

  • 1 | 1.0
  • 1 | -1.0
  • 1 | 2.0
  • 1 | -2.0
  • 1 | 3.0
  • 1 | 2.0
  • 1 | -4.0
  • 1 | -1.0

它的总和为 0.0,行数为偶数,但正面与负面之间不存在一对一的关系。我需要一个查询,它基本上可以告诉我每个正数是否有负数,而无需重用任何行。

我尝试计算数字的不同绝对值并强制它小于所有行的计数,但它并没有捕获所有内容。

我到目前为止的代码:

    DECLARE @tblTest TABLE(
    intGroupID INT
    ,decAmount DECIMAL(19,2)
);

INSERT INTO @tblTest (intGroupID ,decAmount)
VALUES (1,-1.0),(1,1.0),(1,2.0),(1,-2.0),(1,3.0),(1,2.0),(1,-4.0),(1,-1.0);

DECLARE @intABSCount INT = 0
    ,@intFullCount INT = 0;

SELECT @intFullCount = COUNT(*) FROM @tblTest;

SELECT @intABSCount = COUNT(*) FROM (
SELECT DISTINCT ABS(decAmount) AS absCount FROM @tblTest GROUP BY ABS(decAmount)
) AS absCount

SELECT t1.intGroupID
FROM @tblTest AS t1

    /* Make Sure Even Number Of Rows */
    INNER JOIN
    (SELECT COUNT(*) AS intCount FROM @tblTest 
    )
    AS t2 ON t2.intCount % 2 = 0

    /* Make Sure Sum = 0.0 */
    INNER JOIN
    (SELECT SUM(decAmount) AS decSum FROM @tblTest)
    AS t3 ON decSum = 0.0

/* Make Sure Count of Absolute Values < Count of Values */
WHERE 
    @intABSCount < @intFullCount
GROUP BY t1.intGroupID

我认为可能有更好的方法来检查这个表,可能是通过找到对并将它们从表中删除,并在没有更多正/负匹配时查看表中是否还有任何东西,但我宁愿不必使用递归/游标。

4

6 回答 6

1
Create TABLE #tblTest (
    intA INT
    ,decA DECIMAL(19,2)
);

INSERT INTO #tblTest (intA,decA)
VALUES (1,-1.0),(1,1.0),(1,2.0),(1,-2.0),(1,3.0),(1,2.0),(1,-4.0),(1,-1.0), (5,-5.0),(5,5.0) ;


SELECT * FROM #tblTest;

SELECT 
    intA
    , MIN(Result) as IsBalanced
FROM
(
    SELECT intA, X,Result =
          CASE
             WHEN count(*)%2 = 0 THEN 1
             ELSE 0
          END
    FROM
    (
       ---- Start thinking here --- inside-out
       SELECT 
          intA 
          , x = 
             CASE
                WHEN decA < 0 THEN
                    -1 * decA
                ELSE
                    decA
             END 
       FROM #tblTest
    ) t1
    Group by intA, X
)t2
GROUP BY intA
于 2013-07-25T14:56:30.533 回答
1

未经测试,但我认为您可以理解

这会返回不符合的 id
不是更容易测试/调试

select pos.*, neg.* 
  from 
     (  select id, amount, count(*) as ccount
          from tbl 
         where amount > 0 
         group by id, amount ) pos
  full outer join 
     (  select id, amount, count(*) as ccount
          from tbl 
         where amount < 0 
         group by id, amount ) neg
    on pos.id = neg.id 
   and pos.amount = -neg.amount 
   and pos.ccount = neg.ccount
 where pos.id is null 
    or neg.id is null 

I think this will return a list of id that do conform 

select distinct(id) from tbl 
except
select distinct(isnull(pos.id, neg.id)) 
  from 
     (  select id, amount, count(*) as ccount
          from tbl 
         where amount > 0 
         group by id, amount ) pos
  full outer join 
     (  select id, amount, count(*) as ccount
          from tbl 
         where amount < 0 
         group by id, amount ) neg
    on pos.id = neg.id 
   and pos.amount = -neg.amount 
   and pos.ccount = neg.ccount
 where pos.id is null 
    or neg.id is null
于 2013-07-25T15:28:34.040 回答
0

男孩,我找到了一种比我以前的答案更简单的方法。我希望我所有疯狂的编辑都保存下来以供后代使用。

  • 这通过按绝对值(1,-1 由 1 分组)对 id 的所有数字进行分组来实现。
  • 组的总和确定是否存在相等数量的对。如果为 0 则相等,总和的任何其他值都表示存在不平衡。
  • COUNT仅在检测偶数个零时才需要通过聚合检测均匀性。我假设0可能存在并且它们应该出现偶数次。如果这不是问题,请将其删除,因为 0 将始终通过第一个测试。
  • 我用一堆不同的方法重写了查询以获得最佳执行计划。下面的最终结果只有一个大堆排序,由于缺少索引,这是不可避免的。

询问

WITH tt AS (
    SELECT intGroupID, 
        CASE WHEN SUM(decAmount) > 0 OR COUNT(*) % 2 = 1 THEN 1 ELSE 0 END unequal
    FROM @tblTest 
    GROUP BY intGroupID, ABS(decAmount)
)
SELECT tt.intGroupID, 
    CASE WHEN SUM(unequal) != 0 THEN 'not equal' ELSE 'equals' END [pair]
FROM tt
GROUP BY intGroupID;

测试值

(1,-1.0),(1,1.0),(1,2),(1,-2), -- should work
(2,-1.0),(2,1.0),(2,2),(2,2), -- fail, two positive twos
(3,1.0),(3,1.0),(3,-1.0), -- fail two 1's , one -1
(4,1),(4,2),(4,-.5),(4,-2.5), -- fail: adds up the same sum, but different values
(5,1),(5,-1),(5,0),(5,0), -- work, test zeros
(6,1),(6,-1),(6,0), -- fail, test zeros
(7,1),(7,-1),(7,-1),(7,1),(7,1) -- fail, 3 x 1

结果

A   pairs
_   _____
1   equal
2   not equal
3   not equal
4   not equal
5   equal
6   not equal
7   not equal
于 2013-07-25T14:12:15.980 回答
0

这有帮助吗?

-- Expected result - group 1 and 3 
declare @matches table (groupid int, value decimal(5,2))
insert into @matches select 1, 1.0
insert into @matches select 1, -1.0
insert into @matches select 2, 2.0
insert into @matches select 2, -2.0
insert into @matches select 2, -2.0
insert into @matches select 3, 3.0
insert into @matches select 3, 3.5
insert into @matches select 3, -3.0
insert into @matches select 3, -3.5
insert into @matches select 4, 4.0
insert into @matches select 4, 4.0
insert into @matches select 4, -4.0


-- Get groups where we have matching positive/negatives, with the same number of each
select  mat.groupid, min(case when pos.PositiveCount = neg.NegativeCount then 1 else 0 end) as 'Match'
from    @matches mat
LEFT JOIN (select groupid, SUM(1) as 'PositiveCount', Value
                from @matches where value > 0 group by groupid, value) pos 
                on pos.groupid = mat.groupid and pos.value = ABS(mat.value)

LEFT JOIN (select groupid, SUM(1) as 'NegativeCount', Value
                from @matches where value < 0 group by groupid, value) neg 
                on neg.groupid = mat.groupid and neg.value = case when mat.value < 0 then mat.value else mat.value * -1 end

group by mat.groupid
-- If at least one pair within a group don't match, reject
having min(case when pos.PositiveCount = neg.NegativeCount then 1 else 0 end) = 1
于 2013-07-25T14:44:08.880 回答
0

以下应返回“不平衡”组:

;with pos as (
    select intGroupID, ABS(decAmount) m
    from TableName
    where decAmount > 0
), neg as (
    select intGroupID, ABS(decAmount) m
    from TableName
    where decAmount < 0
)
select distinct IsNull(p.intGroupID, n.intGroupID) as intGroupID
from pos p
    full join neg n on n.id = p.id and abs(n.m - p.m) < 1e-8
where p.m is NULL or n.m is NULL

要获得不成对的元素,select可以将状态更改为以下内容:

select IsNull(p.intGroupID, n.intGroupID) as intGroupID, IsNull(p.m, -n.m) as decAmount
from pos p
    full join neg n on n.id = p.id and abs(n.m - p.m) < 1e-8
where p.m is NULL or n.m is NULL
于 2013-07-25T14:48:51.087 回答
0

您可以通过以下方式比较您的值:

declare @t table(id int, amount decimal(4,1))
insert @t values(1,1.0),(1,-1.0),(1,2.0),(1,-2.0),(1,3.0),(1,2.0),(1,-4.0),(1,-1.0),(2,-1.0),(2,1.0)

;with a as
(
select count(*) cnt, id, amount
from @t
group by id, amount
)
select id from @t
except
select b.id from a
full join a b
on a.cnt = b.cnt and a.amount = -b.amount
where a.id is null

出于某种原因,我不能写评论,但是丹尼尔斯的评论是不正确的,我的解决方案确实接受 (6,1),(6,-1),(6,0) 这可能是正确的。0 未在问题中指定,并且由于它是 0 值,因此可以以其他方式处理。我的回答不接受 (3,1.0),(3,1.0),(3,-1.0)

To Blam:不,我没有失踪

or b.id is null

我的解决方案和你的一样,但不完全相同

于 2013-07-25T14:51:24.260 回答