У меня есть одна таблица заказов с 5000 заказами. И у меня есть одна таблица клиентов с 5000 клиентами.
Некоторые заказы имеют пустые значения в поле «CustomerID», которые я хочу исправить с помощью алгоритма.
В настоящее время для каждого нулевого значения в поле «CustomersID» в таблице «Заказы» алгоритм рассматривает три поля из таблицы «Заказы» (1. имя, 2. страна, 3. кластер) и циклически перебирает таблицу клиентов, чтобы найти совпадение с идентичными полями в таблице «Заказы». таблица клиентов (1. имя, 2. страна, 3. кластер). Если есть совпадение, я добавляю «CustomerID» в таблицу заказов (заменяя нулевое значение в «CustomersID», в противном случае я сначала создаю новую запись о клиенте в таблице «Клиенты», а затем добавляю «CustomerID» в таблицу «Заказы»).
Проблема в медленном алгоритме. При циклическом просмотре всех полей с нулевыми значениями в таблице заказов (в худшем случае 5000) и попытке для каждого поля найти совпадение в таблице «Клиенты» (в худшем случае 5000, если совпадений нет), общая временная сложность становится квадратичной. Н^2.
Заранее спасибо! Прикреплен мой код. Первый метод открывает набор записей таблицы заказов (rss). Затем он просматривает набор записей заказов, чтобы найти нулевые значения. Второй метод открывает набор записей для таблицы «Клиенты» (первый). Затем он выполняет поиск совпадений в полях, общих из таблицы заказов (1. имя 2. страна 3. кластер).
Public Sub TransferNullValues() 'hard coded parameters are "Orders", "Customers"
Set coll = New Collection
coll.Add "CustomerID"
coll.Add "End_customer"
coll.Add "End_customer_country"
coll.Add "Cluster"
Dim sqlStr As String
sqlStr = "SELECT " & buildSql(coll) & " FROM Orders where Orders.CustomerID is null;"
coll.Remove (1) 'remove customer id
Dim rss As DAO.Recordset
Set rss = Basic.getRs(sqlStr)
Do While Not rss.EOF
Dim locals As Collection
Set locals = New Collection
Dim v As Variant
For Each v In coll
locals.Add (rss(v).value)
Next
Dim id As Integer
id = getCustID(locals)
rss.Edit
rss("CustomerID").value = id
rss.Update
rss.MoveNext
Loop
rss.Close
Set rss = Nothing
End Sub
Private Function getCustID(locals As Collection) As Integer
Dim sqlStr As String
sqlStr = "SELECT * FROM CUSTOMERS;"
Dim rst As DAO.Recordset
Set rst = Basic.getRs(sqlStr)
Dim trgt As Collection
Do While Not rst.EOF
Set trgt = New Collection
Dim v As Variant
For Each v In coll
trgt.Add (rst(v).value)
Next
Dim allExists As Boolean
allExists = True
For Each v In locals
If Not Basic.Exists(trgt, v) Then
allExists = False
Exit For
End If
Next
If allExists Then
getCustID = rst("CustomerID").value
'Debug.Print "customer exists in customers table"
Exit Function
End If
rst.MoveNext
Loop
rst.AddNew
Dim count As Integer
count = 1
For Each v In locals
rst.Fields(count).value = v
count = count + 1
Next
Dim id As Integer
id = rst("CustomerID")
rst.Update
rst.Close
Set rst = Nothing
getCustID = id
End Function
Я думал о том, как уменьшить временную сложность, но не нашел решения.
Вы можете сделать это более эффективно с помощью запроса UPDATE:
UPDATE Orders
INNER JOIN Customers ON
(Nz(Orders.name) = Nz(Customers.name)) AND
(Nz(Orders.country) = Nz(Customers.country)) AND
(Nz(Orders.cluster) = Nz(Customers.cluster))
SET Orders.CustomerId = Customers.CustomerId
WHERE Orders.CustomerId Is Null
JOIN будет более эффективным, чем O(n2). Я предполагаю, что это O(n·log(n)).
Как отметили комментаторы, вы должны использовать функцию Nz()
для преобразования возможных значений Null
в ненулевые значения (пустую строку или число 0 и т. д.), чтобы сравнения работали правильно.
Поскольку вы не можете присоединиться к NULL, вы можете использовать NZ для замены NULL текстом замены в операторе соединения, например. г. NZ(Orders.cluster, «пустой») = NZ(Customers.cluster, «пусто»)
Это гораздо лучший способ сделать это! Однако мне также нужно принять во внимание ноль. Так, например, строка в таблице «Заказы», где «имя (= null), страна (= Китай), кластер (= null)», должна присоединиться к строке в таблице «Клиенты», где «country (= null), страна (= Китай), кластер (=нуль)"