获得多个唯一值而不分离属于同一值块的值

Posted

技术标签:

【中文标题】获得多个唯一值而不分离属于同一值块的值【英文标题】:get a number of unique values without separating values that belong to the same block of values 【发布时间】:2014-12-04 11:22:21 【问题描述】:

我可以使用 PL/SQL 解决方案或 Access VBA/Excel VBA(尽管 Access VBA 优于 Excel VBA)之一。所以,PL/SQL是第一选择,Access VBA是第二,Excel VBA是第三。

这是一个很难解释的问题。请提出任何问题,我会尽力回答清楚。

我在一个名为 NR_PVO_120 的表中有以下数据集。我如何在不排除任何传真号码下的任何 OtherID 的情况下挑选唯一的其他 ID 的数字(可以更改但比方说 6)?

因此,如果您从第 7 行中选择 OtherID,那么您还必须从第 8 行和第 9 行中选择 OtherID,因为它们具有相同的传真号码。基本上,一旦您选择了一个 OtherID,您就有义务选择与您选择的具有相同传真号码的所有 OtherID。

如果请求的数字(本例中为 6)是不可能的,那么“可能的最接近但不超过的数字”将是规则。

例如,如果您从第 1-10 行获取 OtherID,您将获得 6 个唯一的 OtherID,但第 10 行与第 11 行和第 12 行共享传真。您需要全部获取 3 个(但这会将唯一计数增加到 8 ,这是不可接受的)或跳过此 OtherID 并找到一个传真,该传真将添加 1 个唯一的 OtherID(例如,它可以有 4 个 OtherID,但其中 3 个存在于结果集中,因此不添加到唯一计数中)。我的 6 个唯一 OtherID 的结果将需要在现有 OtherID 连接到的任何传真下包含所有 OtherID。

所以一种解决方案是采用第 1-6、26 行。另一种方法是采用第 1-4,10-14 行。还有更多,但你明白了。

会有很多可能性(真实数据集有数万行,请求的人数将在 10K 左右),只要连接到结果集中所有传真的所有 OtherID 都是请求数的一部分(6在这种情况下)任何组合都可以。

一些笔记。

    要求尽可能接近请求的数字。

    某些 OtherID 将有一个空白传真,它们只能作为最后的手段(没有足够的 OtherID 用于请求的号码)。

这是怎么做到的?

Row      OtherID        Fax
1       11098554    2063504752
2       56200936    2080906666
3       11098554    7182160901
4       25138850    7182160901
5       56148974    7182232046
6       56530104    7182234134
7       25138850    7182234166
8       56148974    7182234166
9       11098554    7182234166
10      56597717    7182248132
11      56166294    7182248132
12      25138850    7182248132
13      56148974    7182390090
14      56226456    7182390090
15      56148974    7182395285
16      25138850    7182395285
17      56166614    7180930966
18      11098554    7180930966
19      56159509    7180930966
20      25138850    7185462234
21      56148974    7185462234
22      25138850    7185465013
23      56024315    7185465013
24      56115247    7185465281
25      25138850    7185465281
26      56148975    7185466029

一些示例输出

一种解决方案是采用第 1-6 行和第 26 行。

Row      OtherID        Fax
1       11098554    2063504752
2       56200936    2080906666
3       11098554    7182160901
4       25138850    7182160901
5       56148974    7182232046
6       56530104    7182234134
26      56148975    7185466029

另一种解决方案是采用第 1-4 行和第 10-14 行。

Row      OtherID        Fax
1       11098554    2063504752
2       56200936    2080906666
3       11098554    7182160901
4       25138850    7182160901
10      56597717    7182248132
11      56166294    7182248132
12      25138850    7182248132
13      56148974    7182390090
14      56226456    7182390090

还有很多。

我只需要传真作为我的输出。

这是一个传真活动,我们需要确保没有传真号码被传真两次,所有连接到该传真号码的人都在发送的一份传真下联系到。

因此,我们的想法是获取您最终使用的任何传真下的所有 OtherID。

编辑这里是它目前是如何完成的,也许这有助于画一幅画

列表是按传真排序的,它们会沿着列表向下随机到达一个点,以确保最后一条记录以相同的传真结束。所以在我的例子中,它们会停在第 1、2、4、5、6、9、12、14、16、19、21、23、25、26 行。然后他们会看到直到那时他们有多少个唯一的 OtherID。如果太多,他们会增加一些,看看他们有多少。如果它太少,他们会下降一些,看看他们有多少。他们一直这样做,直到他们得到他们的唯一号码。唯一的要求是始终在传真下包含所有 OtherID。

【问题讨论】:

请显示您想要的预期输出。 “分解传真号码”并没有多大帮助。 哪个特定的 Oracle 版本 - 11g R1 (11.1) 或 11g R2 (11.2) ? 由于可以有很多组数据来回答这个问题,那么多个有效组中的首选组是什么?随机集?或者如果是最小或最大传真号码的集合是否可以?我猜一组具有最小/最大数字的集合意味着您的传真活动将选择地理上聚集的人,所以您可能需要一个稍微随机的选择? 嗯...当我阅读您关于此问题的另外两个问题时,我认为您希望在您的活动的第一次运行中从您的数据中说 10K 人,那么您将在第二次中选择 10K跑,然后在第三次跑10K,直到你联系了所有人?那么通过查询将每个传真一次性分配给“桶号”而不是重复呼叫是否可以? (这可能更容易。) 然后你说你需要尽可能接近所需的人数。如果您的数据有 41000 人,而您要求每个桶中有 10000 人怎么办?是否需要变成 4 个桶,近万人,然后是第五桶,约 1000 人?或者,如果这变成 5 个桶,每个桶大约有 8200 人,是否可以? 【参考方案1】:

这不是一个完整的答案,但我不想在 cmets 中写很多查询。 您的主要目标是向人们发送信息,并避免一个人收到两次传真的情况。因此,您首先需要一个唯一收件人列表,如下所示:

select distinct otherid
  from NR_PVO_120

如果一个人有两个传真号码,您需要决定选择哪一个:

select otherid, fax
  from (select otherid, fax, row_number() over (partition by otherid order by <choosing rule>) rn
          from NR_PVO_120)
 where rn = 1

(所有这些你在上一个问题的答案中都有) 如果您获取此传真号码列表,则所有收件人都会收到传真,并且每个人只能收到一份传真。但有些传真号码不会被使用。您可以轻松找到它们:

select otherid, fax
  from (select otherid, fax, row_number() over (partition by otherid order by <choosing rule>) rn
          from NR_PVO_120)
 where rn > 1

如果您向其中任何一个号码发送传真,有些人会收到两次传真。 英语不是我的母语,所以我不明白您所说的“不拆分传真号码”是什么意思。正如我在您的问题中看到的那样,您可能需要在问题中使用传真号码的顺序作为号码优先级(表格中的数字越高 - 使用它的可能性越高)。看来您可以使用以下内容:

select otherid, fax
  from (select otherid, fax, row_number() over (partition by otherid order by row) rn
          from NR_PVO_120)
 where rn = 1

order by 子句中的 row 是您的示例表中的 Row

UPD P.S.关于我的最后一个查询:我们有一个有一定顺序的表,顺序很重要。我们逐行获取表格的行。取第一行并将其otheridfax 放入结果表。然后取下一行。如果它包含另一个fax 数字和otherid,我们接受它,如果otherid 已经在我们的结果表中,我们跳过它。你问过这个算法吗?

【讨论】:

大声笑,从我的昵称可以看出,我也会说俄语,英语也不是我的母语。但是,我只是不擅长用任何语言解释事物。 “不拆分传真号码”意味着如果您选择了一个拥有传真 4445555 的人,并且该传真号码也与其他 5 人相关联 - 这 5 个人也必须参与竞选活动。否则,该传真号码将获得该办公室中的一个人的传真,该传真本可以解决所有 6 个问题。然后在最坏的情况下,将发送另外 5 个传真。我现在正在测试您的查询 不,不是这个。我会在几个小时后上班,会弄乱你的功能。我有一个想法,我想我需要在你的循环中添加一些东西来检查以确保我有正确的人数,如果没有,那么继续循环。您的功能完美无缺。唯一的问题是它给我的人比我需要的少,因为一个人有超过 1 份传真。我知道这很令人困惑,我头晕目眩,已经 4 天四夜了,或者我在搞砸它 @lalachka 如果您愿意,您可以尝试在 Skype 中向我解释您想要什么(请参阅我的个人资料)。会快很多……现在看来讨论永远不会结束。 @lalachka 请检查Skype【参考方案2】:

这可以让您在标准 SQL 中实现大部分目标,但它并不是很完美,我希望 MODEL clause 是最有效的,但是...

它的作用是:

    all_possible 中找出所有可能的组合 在some_counting 中旋转这一轮并计算每个fax 中唯一otherids 的数量。我们也可以在此处将其限制为 6 个,以便我们排除任何永远不会符合条件的 faxs 在uniquify 中使用row_number() 以确保我们可以在以后拆分与fax 具有相同数量otherids 的记录,并且也可以得到最大的结果。如果这是 6,那么您就赢了。 在cumulative_sum 中计算每个传真的otherids 数量的运行总和。这里的诀窍是你做它的顺序。我选择先选择最大的,然后添加较小的。我确信有一个更聪明的方法可以做到这一点......我这样做是因为如果最大的是 6,那么你就赢了。如果是 4,那么你可以用 2 个 faxs 填写它,其中只有 1 个关联的 otherid 等。 最后将累积总和限制为 6 条记录,并提取您需要的所有额外数据。

假设一个表格如下,里面填满了你的数据:

create table tmp_table ( 
   r number
 , otherid number
 , fax number
   );

代码如下所示:

with all_possible as (
select t.r as t_r, t.otherid as t_otherid, t.fax as t_fax
     , u.r as u_r, u.otherid as u_otherid, u.fax as u_fax
  from tmp_table t
  left outer join tmp_table u
    on t.fax = u.fax
   and t.r <> u.r
       )
, some_counting as (
 select fax 
      , count(distinct otherid) as no_o_per_fax
   from all_possible
unpivot ( (r, otherid, fax) 
        for (a, b, c)
         in ( (t_r, t_otherid, t_fax)
            , (u_r, u_otherid, u_fax)
            ))
 group by fax
having count(distinct otherid) < 6            
        )
, uniquify as (
select c.*
     , row_number() over (order by no_o_per_fax asc) as rn
     , max(no_o_per_fax) over () as m_fax
  from some_counting c
       )
, cumulative_sum as (
select u.*, sum(no_o_per_fax) over (order by case when no_o_per_fax = m_fax then 0 else 1 end
                                        , no_o_per_fax asc 
                                        , rn ) as csum
  from uniquify u
       )
, candidates as (
select a.*
  from cumulative_sum a
 where csum <= 6
       )
select b.*
  from tmp_table a
  join candidates b
    on a.fax = b.fax

SQL Fiddle

我在这里大量使用公用表表达式来使代码看起来更干净

【讨论】:

我正在等待访问我们的 sql 服务器,以便进行测试。我通常在 oracle 或 vba 中工作,而不是 sql,但没问题,如果可行,我会使用 sql server。 您的问题被标记为 Oracle,它应该在那里工作。 SQL Server 中的 UNPIVOT 不会相同...但是,其余部分应该相同,逻辑也应该相同。 我很困惑,我以为你说这是一个 sql server 解决方案,抱歉大声笑,现在就试试。 知道了,它可以工作,但不够接近。它给了我 5 个,其中 6 个是可能的。与 12 相同,给出 10,其中 12 是可能的。 大声笑我明白了,你说的是标准 sql 而不是 PL SQL?抱歉,我的大脑被这个问题煎炸了【参考方案3】:

如果我正确理解要求,应该这样做。

编辑:我错过了唯一性要求。所以,我已经更新了代码来解决这个问题。

EDIT2:在输出中添加了传真,使用记录类型。

declare
    input_number int := 6;
    cursor get_faxes is
           select fax, count(*) num_ids from listofids
            group by fax
            order by fax;
    cursor get_ids (p_fax in int) is
           select otherid from listofids
             where fax = p_fax;
    type idrec is record(id listofids.otherid%type, fax listofids.fax%type);
    type idlist is table of idrec;
    output_list idlist := idlist();
    v_memberof  boolean;
begin
    for fax_rec in get_faxes loop
        if output_list.count + fax_rec.num_ids <= input_number then
            for id_rec in get_ids(fax_rec.fax) loop
                v_memberof := False;
                for i in 1..output_list.count loop
                    if output_list(i).id = id_rec.otherid then
                        v_memberof := true;
                    end if;
                end loop;
                if not v_memberof then
                    output_list.extend(1);
                    output_list(output_list.count).id := id_rec.otherid;
                    output_list(output_list.count).fax := fax_rec.fax;
                end if;
            end loop;
        end if;
    end loop;
    for i in 1..output_list.last loop
        dbms_output.put_line('id: ' || output_list(i).id || '  fax:' || output_list(i).fax);
    end loop;
end;

现在返回以下内容:

id: 11098554  fax:2063504752
id: 56200936  fax:2080906666
id: 56166614  fax:7180930966
id: 56159509  fax:7180930966
id: 25138850  fax:7182160901
id: 56148974  fax:7182232046

如果您确实需要随机选择,您可以更改顺序以使用 dbms_random.random 代替传真。

【讨论】:

我只得到了 3 个唯一的otherids,结果给出了 6 个但它们不是唯一的 我错过了这个要求。对不起!我已经更新了代码。让我知道是否这样做,或者我是否错过了其他内容。 完全没有问题,感谢您的尝试。你能给我传真号码作为输出吗?我得到了错误的结果,但我不知道为什么,除非我知道您选择了哪些传真。对不起:) 已编辑以将传真添加到输出。希望这会有所帮助。再次,让我知道接下来需要发生什么。这变成了一个有趣的问题。 抱歉,刚看到这个,正在测试。这肯定很有趣,在过去的 10 天里我想不出其他任何事情 :)【参考方案4】:

2015 年 2 月 13 日编辑 在使用接受的答案几个月后,我遇到了一个尚未发生的场景,并意识到他的解决方案只有在我需要得到一个不太接近总数的数字时才有效。例如,如果我的记录总数为 15000 并且我要求 12000,那么他的代码将给出 10 或 11k。如果我要 8k,那么我可能会得到 8。

我不明白他的代码是做什么的,他从来没有回复过,所以我无法解释为什么会这样,我的猜测是他按特定顺序进行计数,因为结果取决于传真的顺序被排序——他不一定每次都能得到最好的结果。 当有足够的空间时(要求 15k 中的 8l),他有足够的空间来进行任何组合以产生可接受的结果,但是一旦您要求更小的数字(15k 中的 12k),他就会被锁定在他的订单中并且足够快地用完可接受的计数.

所以这是无论如何都会给出正确结果的代码。它没有那么优雅,而且速度极慢,但它确实有效。

12/13/14 我想我明白了,PL/SQL,到目前为止不是最好的解决方案,但它提供的结果比他们目前手动获得的结果更好。实际上,我真的很想知道可能的问题

2014 年 12 月 13 日编辑接受的答案是这样做的方法,我只是把这个留在这里作为对比,所以人们可以看到如何不编码大声笑。

DECLARE
     CountsNeededTotal NUMBER;
     CountsNeededRemaining NUMBER;
     CurCountsTotal NUMBER;
     CurFaxCount NUMBER;
     CurFaxCountPicked NUMBER;
BEGIN
     CountsNeededTotal := 420;
     CurCountsTotal := 0;
     CurFaxCount := 0;

     CountsNeededRemaining := CountsNeededTotal - CurCountsTotal;

     EXECUTE IMMEDIATE 'TRUNCATE TABLE NR_PVO_121';


     --############################################################################################
     --############################################################################################
     --############################################################################################
     --############################################################################################
     --############################################################################################
     --START BLOCK
     --this block jsut gets the first fax, the fax with the largest number of people
     --############################################################################################
     --############################################################################################
     --############################################################################################
     --############################################################################################
     --############################################################################################

     --get the first fax with the most people as long as thta number isn't larger than the number needed
     SELECT MAX(CountOfPeople) CountOfPeople
    INTO CurFaxCount
    FROM (SELECT     fax
            ,COUNT(1) CountOfPeople
           FROM NR_PVO_120
          GROUP BY Fax
         HAVING COUNT(1) <= CountsNeededRemaining);

     COMMIT;

     --if there is a number that's not larger then add to the table and keep looping
     --if there isn't then there's no providers from this campaign that can be used
     IF CurFaxCount >= 0 THEN
       --insert into the 121 table (final list of faxes)
       INSERT INTO NR_PVO_121
         SELECT   fax
              ,COUNT(1) CountOfPeople
             FROM NR_PVO_120
           HAVING COUNT(1) = (SELECT MAX(CountOfPeople) CountOfPeople
                       FROM (SELECT   fax
                               ,COUNT(1) CountOfPeople
                              FROM NR_PVO_120
                          GROUP BY Fax
                            HAVING COUNT(1) <= CountsNeededTotal))
         GROUP BY Fax;



       COMMIT;

       --############################################################################################
       --############################################################################################
       --############################################################################################
       --############################################################################################
       --############################################################################################
       --START BLOCK
       --this block loops through remaining faxes
       --############################################################################################
       --############################################################################################
       --############################################################################################
       --############################################################################################
       --############################################################################################



       SELECT SUM(CountOfPeople) INTO CurCountsTotal FROM NR_PVO_121;


       IF CurCountsTotal < CountsNeededTotal THEN
         CountsNeededRemaining := CountsNeededTotal - CurCountsTotal;


         --loop until counts needed remaining is 0 or as close as 0 as possible without going in the negative
         WHILE CountsNeededRemaining >= 0 LOOP
              --clear 122 table
              EXECUTE IMMEDIATE 'TRUNCATE TABLE NR_PVO_122';


              --loop through all faxes in 120 table  MINUS the ones in the 121 table
              DECLARE
                CURSOR CurRec  IS
                  SELECT DISTINCT Fax
                    FROM NR_PVO_120
                   WHERE Fax NOT IN (SELECT Fax FROM NR_PVO_121);
                PVO CurRec%ROWTYPE;
              BEGIN
                OPEN CurRec;
                LOOP
                  FETCH CurRec INTO PVO;

                  SELECT DISTINCT COUNT(OtherID) CountOfPeople
                    INTO CurFaxCount
                    FROM NR_PVO_120
                   WHERE     Fax = PVO.fax
                      AND OtherID NOT IN (SELECT DISTINCT OtherID
                                   FROM NR_PVO_120
                                  WHERE fax IN (SELECT Fax FROM NR_PVO_121));
                  --                                                          DBMS_OUTPUT.put_line('CurFaxCount ' || CurFaxCount);
                  --                                                          DBMS_OUTPUT.put_line('CountsNeededRemaining ' || CountsNeededRemaining);

                  IF CurFaxCount <= CountsNeededRemaining THEN
                    --record their unique counts in 122 table IF THEY'RE NOT LARGER THAN CountsNeededRemaining
                    INSERT INTO NR_PVO_122
                         SELECT PVO.fax
                            ,CurFaxCount
                        FROM DUAL;

                    COMMIT;
                  END IF;
                  EXIT WHEN CurRec%NOTFOUND;
                --end fax loop
                END LOOP;
                CLOSE CurRec;
              END;


              --pick the highest count from 122 table
              SELECT MAX(CountOfPeople) CountOfPeople INTO CurFaxCountPicked FROM NR_PVO_122;

              --add this fax to the 121 table
              INSERT INTO NR_PVO_121
                SELECT MIN(Fax) Fax
                   ,CurFaxCountPicked
                  FROM NR_PVO_122
                 WHERE CountOfPeople = CurFaxCountPicked;


              COMMIT;
              --add the counts to the CurCountsTotal
              CurCountsTotal := CurCountsTotal + CurFaxCountPicked;
              --recalc   CountsNeededRemaining
              CountsNeededRemaining := CountsNeededTotal - CurCountsTotal;
              --
              --                                                          DBMS_OUTPUT.put_line('CurCountsTotal ' || CurCountsTotal);
              --                                                          DBMS_OUTPUT.put_line('CurFaxCountPicked ' || CurFaxCountPicked);
              --                                                          DBMS_OUTPUT.put_line('CurFaxCount ' || CurFaxCount);
              --                                                          DBMS_OUTPUT.put_line('CountsNeededRemaining ' || CountsNeededRemaining);
              --                                                          DBMS_OUTPUT.put_line('CountsNeededTotal ' || CountsNeededTotal);

              --clear 122 table
              EXECUTE IMMEDIATE 'TRUNCATE TABLE NR_PVO_122';
         --end while loop
         END LOOP;
       END IF;
     --############################################################################################
     --############################################################################################
     --############################################################################################
     --############################################################################################
     --############################################################################################
     --END BLOCK
     --this block loops through remaining faxes
     --############################################################################################
     --############################################################################################
     --############################################################################################
     --############################################################################################
     --############################################################################################



     END IF;
--############################################################################################
--############################################################################################
--############################################################################################
--############################################################################################
--############################################################################################
--END BLOCK
--this block jsut gets the first fax, the fax with the largest number of people
--############################################################################################
--############################################################################################
--############################################################################################
--############################################################################################
--############################################################################################



END;

这里有一个更好的版本,比上面的要快得多,但在某些情况下它可能不会返回完美的结果。我在测试时无法得到错误的结果,但有可能是因为我没有尝试所有可能的组合(如在第一个版本中),这需要几天才能完成 20K 记录的数据集

DECLARE
    CountsNeededTotal NUMBER;
    CountsNeededRemaining NUMBER;
    CurCountsTotal NUMBER;
BEGIN
    CurCountsTotal := 0;

    SELECT NoOfProvToKeep INTO CountsNeededTotal FROM NR_PVO_121;

    CountsNeededRemaining := CountsNeededTotal - CurCountsTotal;

    EXECUTE IMMEDIATE 'TRUNCATE TABLE nr_pvo_122';


    COMMIT;

    IF CurCountsTotal <= CountsNeededTotal THEN
        --loop until counts needed remaining is 0 or as close as 0 as possible without going in the negative
        WHILE CountsNeededRemaining > 0 LOOP
            --clear 122 table
            INSERT INTO NR_PVO_122
                SELECT Fax
                      ,CountOfPeople
                  FROM (SELECT   DISTINCT COUNT(OtherID) CountOfPeople
                               ,Fax
                       FROM NR_PVO_120
                      WHERE OtherID NOT IN (SELECT DISTINCT OtherID
                                    FROM NR_PVO_120
                                   WHERE fax IN (SELECT Fax FROM NR_PVO_122))
                     HAVING COUNT(1) <= CountsNeededRemaining
                        GROUP BY fax
                        ORDER BY 1 DESC)
                 WHERE ROWNUM = 1;



            SELECT SUM(CountOfPeople) INTO CurCountsTotal FROM NR_PVO_122;

            COMMIT;
            --recalc   CountsNeededRemaining
            CountsNeededRemaining := CountsNeededTotal - CurCountsTotal;
        --
        --DBMS_OUTPUT.put_line('CurCountsTotal ' || CurCountsTotal || ', CountsNeededRemaining ' || CountsNeededRemaining);
        --end while loop
        END LOOP;
    END IF;



    DELETE FROM NR_PVO_112
          WHERE NVL(Fax, '999999999999') NOT IN (SELECT Fax FROM NR_PVO_122);
END;

【讨论】:

【参考方案5】:

不确定您的要求,但这是我对您的问题的理解最好的。 首先代码对传真上的数据进行排序,然后提取第一次出现传真的 ID,即使在此之后,由于数据存在重复的 ID,所以再次进行排序和删除重复项。

Sub Unique_fax()

找到最后一行以便循环可以运行多次

lastrow = Worksheets("Sheet1").Cells(Rows.Count, 1).End(xlUp).Row

将数据复制到新行,以便您的原始数据保持不变

For i = 1 To lastrow

Worksheets("Sheet1").Cells(i, 5).Value = Trim(Worksheets("Sheet1").Cells(i, 1))
Worksheets("Sheet1").Cells(i, 6).Value = Trim(Worksheets("Sheet1").Cells(i, 2))
Worksheets("Sheet1").Cells(i, 7).Value = Trim(Worksheets("Sheet1").Cells(i, 3))

Next

根据传真对数据进行排序

Range("E1:G" & lastrow).Select
    Selection.Sort Key1:=Range("G1"), Order1:=xlAscending, _
                   Header:=xlNo, OrderCustom:=1, MatchCase:=False, Orientation:=xlTopToBottom

将传真不同的 ID 复制到新行

x = 1
For i = 1 To lastrow
If Cells(i, 7) <> Cells(i + 1, 7) Then
Cells(x, 9) = Cells(i, 6)
x = x + 1
End If
Next

对 ID 列表进行排序并删除重复项

lastrowUnq = Worksheets("Sheet1").Cells(Rows.Count, 9).End(xlUp).Row

Range("I1:I" & lastrowUnq).Select
    Selection.Sort Key1:=Range("I1"), Order1:=xlAscending, _
                   Header:=xlNo, OrderCustom:=1, MatchCase:=False, Orientation:=xlTopToBottom
y = 1
For j = 1 To lastrow
If Cells(j, 9) <> Cells(j + 1, 9) Then
Cells(y, 11) = Cells(j, 9)
y = y + 1
End If
Next

End Sub

列 - A、B、C 是您的原始数据。 列 - E、F、G 是在传真上排序的数据。 列 - I 包含传真唯一的 ID 列表。 列 - K 包含最终的 ID 列表(根据需要)。

【讨论】:

首先,感谢您的尝试 :) 但我不明白结果是什么。 “传真是独一无二的”是什么意思?另外,我最终会寻找特定数量的唯一 OtherID(数字将始终更改),其中包括连接到最终列表中的任何传真的所有 OtherID。 所以相同的 ID 有重复的“Fax”,我的程序正在提取最后一个“Fax”(见 F 和 G 列)和对应的 ID,之后仍然有重复的 ID (见第 I 列)然后它在 K 列中保留唯一 ID。您是否期望由此产生不同的结果? 从本质上讲,它正在做您所期望的事情 - 即“我们需要确保没有传真号码被传真两次,所有连接到该传真号码的人都会在发送的一份传真下联系到”。跨度> 不,我只是不明白我在验证什么。您可能误解了我在说什么,每次我试图解释它时都会发生这种情况:) 抱歉。因此,您选择了第 19 行和第 18 行(之前选择过),但没有选择第 17 行,这意味着此人不会出现在列表中,不会包含在传真中(亲爱的第 18,17 和 19 行),不会标记为“已传真”,并将在下一次传真中心请求此状态时再次发送到另一个列表中。你明白我在说什么吗?抱歉,我不知道该怎么解释【参考方案6】:

在开始时测试的数据。注意 OtherID 在 Col A 中,Fax 在 Col B 中: 首先,我们将找到您想要的唯一 ID 的数量 注意:您将需要一张名为“使用我”的新表格。为此,我们将需要一个自定义函数。该函数可以作为单元格公式运行,语法为 =UniqueItems(B2:D5),但我们将在 Sub 中使用它:

Function UniqueItems(ArrayIn, Optional Count As Variant) As Variant
'   Accepts an array or range as input
'   If Count = True or is missing, the function returns the number of unique elements
'   If Count = False, the function returns a variant array of unique elements
    Dim Unique() As Variant ' array that holds the unique items
    Dim Element As Variant
    Dim i As Integer
    Dim FoundMatch As Boolean
'   If 2nd argument is missing, assign default value
    If IsMissing(Count) Then Count = True
'   Counter for number of unique elements
    NumUnique = 0
'   Loop thru the input array
    For Each Element In ArrayIn
        FoundMatch = False
'       Has item been added yet?
        For i = 1 To NumUnique
            If Element = Unique(i) Then
                FoundMatch = True
                Exit For '(exit loop)
            End If
        Next i
AddItem:
'       If not in list, add the item to unique list
        If Not FoundMatch And Not IsEmpty(Element) Then
            NumUnique = NumUnique + 1
            ReDim Preserve Unique(NumUnique)
            Unique(NumUnique) = Element
        End If
    Next Element
'   Assign a value to the function
    If Count Then UniqueItems = NumUnique Else UniqueItems = Unique
End Function

这里是您需要找到您的唯一 ID 并将它们复制到“使用我”表中的子代码

Sub FaxesToUse()
    Dim LastRow As Long, CurRow As Long, UniqueTotal As Long, SubTotal As Long

    UniqueTotal = InputBox("How Many Unique OtherIDs is Max?")
    If Not UniqueTotal > 0 Then
        Exit Sub
    End If

    LastRow = Range("A" & Rows.Count).End(xlUp).Row
    SubTotal = 0
    For CurRow = 2 To LastRow
        SubTotal = UniqueItems(Range("A2:A" & CurRow))
        If SubTotal > UniqueTotal Then
            SubTotal = UniqueItems(Range("A2:A" & CurRow - 1))
            Range("A1:B" & CurRow - 1).Copy
            Sheets("Use Me").Cells.Clear
            Sheets("Use Me").Range("A1").PasteSpecial xlPasteValues
            Sheets("Use Me").Activate
            MsgBox "Use Me Sheet rows contain " & SubTotal & " Unique OtherIDs"
            Exit Sub
        End If
        Cells(CurRow, 1).EntireRow.Interior.Color = RGB(255, 255, 0)
    Next CurRow

End Sub

这将为您提供一个如下所示的页面: 现在我们只需要使用此宏删除所有重复的传真:

Sub RemoveDups()

Dim CurRow As Long, LastRow As Long, LastCol As Long, DestLast As Long, DestRng As Range, ws As Worksheet

Set ws = Sheets("Use Me")
LastRow = ws.Range("A" & Rows.Count).End(xlUp).Row

For CurRow = LastRow To 3 Step -1
     Set DestRng = ws.Range("B2:B" & CurRow - 1).Find(ws.Range("B" & CurRow).Value, LookIn:=xlValues, LookAt:=xlWhole, SearchDirection:=xlNext)
     If DestRng Is Nothing Then
         'Do Nothing
     Else
        DestLast = ws.Cells(DestRng.Row, Columns.Count).End(xlToLeft).Column + 1
        ws.Cells(DestRng.Row, DestLast).Value = ws.Cells(CurRow, 1).Value
        ws.Cells(CurRow, 1).EntireRow.Delete xlShiftUp
     End If
     Next CurRow
ws.Columns("B:B").Cut
ws.Columns("A:A").Insert Shift:=xlToRight
Application.CutCopyMode = False

LastRow = ws.Range("A" & Rows.Count).End(xlUp).Row
LastCol = 0
For CurRow = 2 To LastRow
    If ws.Cells(CurRow, Columns.Count).End(xlToLeft).Column > LastCol Then
        LastCol = ws.Cells(CurRow, Columns.Count).End(xlToLeft).Column
    End If
Next CurRow

MsgBox "Use Me Sheet Rows contain " & UniqueItems(ws.Range(Cells(2, 2), Cells(LastRow, LastCol))) & " Unique OtherIDs"

End Sub

留给你这个:

【讨论】:

我现在要进行测试,但由于您给了我唯一的 ID,这才是最重要的,我将使用 thrm 并将它们映射回我的列表,并为这些人获取所有传真。如果这样做正确,那么他的任何传真中的任何人也将在列表中,所以一切都会解决。 不,我错了,刚刚与手动执行此操作的人交谈过,但过程中有错误。你是对的,选择哪个很重要。他们正在做的是通过传真进行分类并顺势而为。所以他们接受传真,拿走所有 OtherID,从 OtherID 中删除 dups,计数,不够,下一步,从 OtherID 中删除 dups,计数,不够,拿走所有 OtherID,下一个......所以对他们来说,这个问题不存在,因为他们从传真号码开始,然后从中挑选所有人。 所以我的流程有效,如果已经传真(在流程的早期),您只需要删除 ID? 马上要测试了,抱歉,我想从我的工作中得到澄清。 它仍在运行(我的数据集很大),但我想我知道发生了什么。我有autoids,我想我应该把它们留在里面。所以让我们看看会发生什么。

以上是关于获得多个唯一值而不分离属于同一值块的值的主要内容,如果未能解决你的问题,请参考以下文章

Python 3:检查迭代器的下一个值而不进行迭代

提交文本框的值而不重新加载页面

如何查询按核心数据中唯一值分组的值?

仅使用 jQuery/Ajax 刷新表的值而不重新加载页面

在 MongoDB 中搜索任何字段的值而不显式命名它

在 Sencha Touch 中设置 numberfield 的值而不触发“change”事件