我可以使用PL / SQL解决方案或Access VBA / Excel VBA(虽然Access VBA优于Excel VBA)。所以,PL / SQL是第一选择,Access VBA是第二选择,Excel VBA是第三选。
这是一个非常难以解释的问题。请提出任何问题,我会尽力回答。
我在名为NR_PVO_120的表中有以下数据集。我如何在不排除任何传真号码下的任何其他ID的情况下挑选出一个UNIQUE OtherID的号码(可以改变但可以改为6)?
因此,如果您从Row7中选择OtherID,那么您还必须从第8行和第9行中选择OtherID,因为它们具有相同的传真号码。基本上,一旦您选择了OtherID,您就有义务选择与您选择的传真号码相同的所有其他ID。
如果请求的号码(本例中为6)不可能,那么"最接近的号码但不超过"将是规则。
例如,如果您从第1-10行获取OtherID,您将获得6个唯一的OtherID,但第10行与第11行和第12行共享传真。您需要全部使用3(但这会将唯一计数提高到8) ,这是不可接受的)或跳过此OtherID并找到一个传真,将添加1个唯一的OtherID(例如,它可以有4个OtherID,但结果集中存在3个,因此不要添加到唯一计数)。我的6个UNIQUE OtherID的结果将需要包含现有OtherID所连接的任何传真下的所有OtherID。
因此,一种解决方案是采用1-6行,26行。另一种方法是采用1-4,10-14行。还有更多,但你明白了。
将有许多可能性(真实数据集有数万行,请求的人数大约为10K),只要连接到结果集上所有传真的所有其他ID都是所请求号码的一部分(6在这种情况下)任何组合都可以。
一些笔记。
要求尽可能接近所要求的号码。
某些其他ID会有一个空白传真,它们只应作为最后的手段包含在内(对于所请求的号码,其他ID不够)。
这是怎么做到的?
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
还有更多。
我只需要传真作为输出。
这是针对传真广告系列,我们需要确保没有传真号码传真两次,连接到该传真号码的所有人都会通过一个传真发送。
因此,我们的想法是在您最终使用的任何传真下使用所有其他ID。
编辑这里目前的工作方式,也许这有助于画一幅画
列表按传真排序,它们从列表中下载到随机点,确认最后的记录与相同的传真结束。所以在我的例子中,他们会停在第1,2,4,5,6,9,12,14,16,19,21,23,25,26行。然后,他们会看到他们在此之前有多少独特的OtherID。如果它太多了,那就看看它们有多少。如果它太少,它们会下降一些,看看它们有多少。他们一直这样做,直到他们得到他们唯一的号码。唯一的要求是始终在传真中包含所有其他ID。
答案 0 :(得分:1)
这不是一个完整的答案,但我不想在评论中写下很多疑问 您的主要目标是向人们发送信息,并避免一个人收到传真两次的情况。所以你首先需要一个独特的收件人列表,如下所示:
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
如果您将传真发送到任何此号码,有些人会收到两次传真 英语不是我的母语,所以我不明白你说什么,而不会打破传真号码&#34;。正如我在你的问题中所看到的,你可能需要在你的问题中使用传真号码的顺序作为数字优先级(表中的数字越大 - 使用它的概率越高)。看起来你可以使用以下内容:
select otherid, fax
from (select otherid, fax, row_number() over (partition by otherid order by row) rn
from NR_PVO_120)
where rn = 1
此row
子句中的order by
是示例表中的Row
。
<强> UPD 强>
P. S.关于我的上一个问题:我们有一个具有特定顺序的表,顺序很重要。我们逐行获取表格的行。取第一行并将其otherid
和fax
放到结果表中。然后采取下一行。如果它包含另一个fax
号码和otherid
,我们会接受它,如果我们的结果表中已经otherid
,我们会跳过它。你问过这个算法吗?
答案 1 :(得分:1)
这可以让你在标准SQL中获得大部分内容,但它并不完美,我希望MODEL clause最适合,但是...
这是做什么的,是:
all_possible
中计算出所有可能的组合some_counting
中进行调整,并计算每otherid
个唯一fax
的数量。我们也可以将此限制为6,以便我们排除任何永远不会符合条件的fax
uniquify
中使用row_number()
以确保我们可以在以后otherid
分割具有相同fax
个数量的记录,并确定最佳记录。如果这是6,那么你就获得了简单的胜利。cumulative_sum
中计算出每个传真otherid
个的运行总和。这里的诀窍是你执行它的顺序。我选择先选择最好的,然后加入较小的。我确信有一种更聪明的方法可以做到这一点......我这样做是因为如果最大的是6,你就赢了。如果它是4,那么你可以用2 fax
个填充它,只有1个otherid
等。假设一张表如下,填写了您的数据:
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
我在这里广泛使用公用表表达式来使代码看起来更干净
答案 2 :(得分:1)
如果我理解正确的要求,就应该这样做。
编辑:我错过了唯一性要求。所以,我已经更新了代码来解释这个问题。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 :(得分:1)
编辑2/13/2015 在使用接受的答案几个月后,我遇到了一个尚未发生的情景,并意识到他的解决方案只有在我需要得到一个不太接近总数的数字时才有效。例如,如果我的总记录数是15000并且我要求12000,那么他的代码将给出10或11k。如果我要求8k,那么我可能会得到8。
我不明白他的代码是做什么的,他从不回复所以我无法解释为什么会发生这种情况,我的猜测是他按照一定的顺序接受了计数,因为结果取决于传真的排序顺序 - 他不一定每次都能得到最好的结果。 当有足够的空间(从15k中获得8l)时,他有足够的空间进行任何组合以产生可接受的结果但是一旦你要求更紧的数字(15k中的12k),他就会被锁定在他的命令中并且足够快地用完了可接受的计数。
所以这是无论如何都能得到正确结果的代码。它不是那么优雅,而且速度极慢但是很有效。
12/13/14我认为我得到它,PL / SQL,到目前为止不是最好的解决方案,但它提供了比他们目前手工获得的更好的结果。实际上,真的很想知道可能存在的问题12/13/14编辑接受的答案就是这样做的方式,我只是留在这里作为对比,所以人们可以看到如何不编码大声笑。
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;
答案 4 :(得分:0)
不确定您的要求,但这是我理解您的最佳问题。 首先,代码在传真上对数据进行排序,然后首次提取传真出现的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是在传真上排序的数据。 列 - 我包含传真唯一的ID列表。 列 - K包含最终的ID列表(根据需要)。
答案 5 :(得分:0)
开始时测试的数据。注意其他ID在Col A中,在Col B中为传真:
首先,我们将找到您想要的唯一ID的数量注意:您需要一个新的表格,并且#34;使用我&#34;。我们需要一个自定义功能。此函数可以作为具有语法=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并将其复制到工作表中的子项&#34;使用我&#34;
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
请留下这个: