get a number of unique values without separating values that belong to the same block of values

后端 未结 6 524
伪装坚强ぢ
伪装坚强ぢ 2020-12-19 15:06

I\'m OK with either a PL/SQL solution or an Access VBA/Excel VBA (though Access VBA is preferred over Excel VBA) one. so, PL/SQL is the first choice, Access VBA is second a

6条回答
  •  Happy的楠姐
    2020-12-19 15:41

    EDIT 2/13/2015 after using the accepted answer for a few months i came across a scenario that hasn't happened yet and realized that his solution only works if i need to get a number that's not too close to the total. for example, if my total number of records is 15000 and i'm asking for 12000 then his code will give 10 or 11k. if i ask for 8k then i will probably get the 8.

    i don't understand what his code does and he never replied so i can't explain why this is happening, my guess is that he's taking the counts in a certain order and since the results are dependent on the order the faxes are sorted in - he won't necessarily get the best results every time. when there's enough room (asking 8l out of 15k) he has enough room for any combination to yield the acceptable result but once you ask for a tighter number (12k out of 15k) he's locked into his order and runs out of acceptable counts fast enough.

    so this is the code that will give correct result no matter what. it's not nearly as elegant and is extremely slow but it works.

    12/13/14 i think i got it, PL/SQL, not the best solution by far but it gives better results than what they currently get by hand. actually, would be really interested to hear about possible problems

    12/13/14 EDIT the accepted answer is the way to do it, i'm only leaving this here for contrast, so people can see how not to code lol.

    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;
    

    here's a better version, MUCH faster than the above but it probably won't return perfect results in some cases. i wasn't able to get wrong results while testing but there is a possibility because i'm not trying every possible combination (as in the first version), that takes days to finish for a dataset of 20K records

    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;
    

提交回复
热议问题