Страницы: 1
RSS
Расчет числа деталей по спецификациям
 

Добрый вечер, есть задача расчета деталей и подсборок, образующих одну единую сборку. Например, в велосипед (верхнюю сборку) входит несколько колес (подсборка), в свою очередь в колеса входят спицы и т. д. Надо посчитать и общее количество и спиц, и колес, и всех остальных деталей и подсборок.

Сейчас в Excel забиты спецификации на изделие. В первом столбце обозначение элемента (детали, сборки). Во втором столбце – обозначение его «родителя». В третьем столбце – количество элементов, входящих в родительский узел. Например, запись «.000 .300 3» означает, что в состав изделия «.000» входит 3 изделия «.300». При этом один элемент может иметь и несколько родителей, в таких случаях надо складывать количество, входящее в каждую сборку, и находить общее количество.

Результат выводится в виде таблицы, где для каждого элемента указано его общее количество в сборке .000. В идеале при выводе результата может выводиться и столбец «детализация», где подробно расписывается, в какие именно сборки и в каком количестве входит деталь. Но в принципе и вариант без этого столбца будет полезен. Можно ли решить задачу без макросов?

 
kirillkuts, Добрый вечер.
Не понял как такие результаты получились, если нужно просуммировать все записи с .001 то можно массивной
Код
=СУММ(($L$10:$L$24=Q10)*$M$10:$M$24)

или немассивной
Код
=СУММЕСЛИ($L$10:$L$24;Q10;$M$10:$M$24)

результат 23
Изменено: Hugo - 10.02.2026 00:25:20
 
еще один вариант результата...
Пришелец-прораб.
 
задача просто идеальна для макросов, жаль что нужно без них
 
а почему бы не так?)
 
Цитата
написал:
еще один вариант результата...
Да, вот здесь правильно подсчитано, но нужен не просто ответ а способ автоматического решения)
 
Цитата
написал:
задача просто идеальна для макросов, жаль что нужно без них
а с макросами сильно сложно будет? просто очень давно поверхностно с ними знакомился, уже ничего не помню по этой теме, но по необходимости могу разобраться
 
Цитата
kirillkuts написал:
способ автоматического решения
боюсь, что вам он не подойдет - слишком сложно получилось. Группировка, словарь, рекурсия... Одни неприятности. Впрочем, извольте...
код
Пришелец-прораб.
 
Цитата
написал:
а с макросами сильно сложно будет?
Нет.
Код
Option Explicit

Function КОЛИЧЕСТВОДЕТАЛЕЙ(изделия As Range, количество_изделий As Range, деталь As String, спецификации As Range) As Double
    Static dicSpec As Object
    If dicSpec Is Nothing Then
        Dim aSpec As Variant
        aSpec = спецификации.Resize(, 3).Value
        Set dicSpec = GetDicSpec(aSpec)
    End If
    Dim yi As Long, изделие As String
    For yi = 1 To изделия.Rows.Count
        изделие = изделия.Cells(yi, 1).Value
        If dicSpec.Exists(изделие) Then
            If dicSpec(изделие).Exists(деталь) Then
                КОЛИЧЕСТВОДЕТАЛЕЙ = КОЛИЧЕСТВОДЕТАЛЕЙ + dicSpec(изделие)(деталь) * количество_изделий.Cells(yi, 1).Value
            End If
        End If
    Next
End Function

Private Function GetDicSpec(aSpec As Variant) As Object
    Dim dic As Object
    Set dic = CreateObject("Scripting.Dictionary")
    
    Dim ya As Long
    For ya = 1 To UBound(aSpec, 1)
        If Not dic.Exists(aSpec(ya, 1)) Then
            Set dic(aSpec(ya, 1)) = CreateObject("Scripting.Dictionary")
        End If
        dic(aSpec(ya, 1))(aSpec(ya, 2)) = aSpec(ya, 3)
    Next
    
    Dim bb As Variant, cc As Variant, ff As Variant, bic As Object, fic As Object
    For Each bb In dic.Keys
        Set bic = dic(bb)
        For Each cc In bic.Keys
            If dic.Exists(cc) Then
                Set fic = dic(cc)
                For Each ff In fic.Keys
                    dic(bb)(ff) = dic(bb)(ff) + fic(ff) * dic(bb)(cc)
                Next
            End If
        Next
        dic(bb)(bb) = 1
    Next
    
    Set GetDicSpec = dic
End Function

Изменено: МатросНаЗебре - 11.02.2026 11:42:38 (If dicSpec Is Nothing Then)
 
Цитата
kirillkuts написал:
Да, вот здесь правильно подсчитано,
- а вот как должно считаться так и не рассказали.
Потому что догадки конечно у нас есть, но в примере результат совсем не такой показан. И кстати макрос тоже не так считает - не сходится с PQ
 
Там ещё и индексация столбцов своеобразная.
Цитата
написал:
В первом столбце обозначение элемента (детали, сборки). Во втором столбце – обозначение его «родителя».
Во вложенном файле родитель находится в левом столбце. Получается левый столбец это второй, соответственно правый - это первый.
Не запрещено, конечно :)))
 
Hugo, все пгосто: "посадил дед репку" (.000). Она состоит из элементов в правом (втором по счету) столбце спецификации. Сами элементы могут как напрямую участвовать в создании .000 (пары .000 - "элемент"), так и быть составной частью других элементов. Задача - посчитать кол-во каждого элемента в "репке". Кол-во указано в 3м столбце спецификации. Пример: элемент .110. В таблице он используется для
.200 : 2 шт на каждый из .200, а самого .200 нужно 2 шт на каждый .000. Т.е. нужно 2 х 2 = 4 элемента .110 для создания необходимого кол-ва .200
.100 : 1 шт на каждый из .100, а самого .100 нужно тоже 1 шт на каждый .000. Т.е. нужно 1 х 1 = 1 элемент .110 для создания необходимого кол-ва .100 в спецификации.
Ответ: всего .110 нужно 4 + 1 = 5 шт, из них 4 в .200 и 1 в .100
Сам ТС с таким расчетом не справился. Бывает...
Изменено: AlienSx - 11.02.2026 11:50:08
Пришелец-прораб.
 
Ну я тоже так предполагал, смотрел пример - не сходится ни так, ни эдак...
Решил подождать, я не тороплюсь.
Начал разбор с .001 и сразу мимо - никак не 30 в .110 ((

разложил .001
 
Цитата
AlienSx:    еще один вариант результата...
kirillkuts:    Да, вот здесь правильно подсчитано
AlienSx, этот ребус вам в зачёт  ;)
Цитата
kirillkuts:   Можно ли решить задачу без макросов?
Можно, но сложность решения зависит от количества уровней вложенности
 
Цитата
Hugo написал:
никак не 30 в .110 ((
и у меня не 30, а 50. И сумма тоже 75, а не 55. Вот я и попридержал "коней" до выяснения...
Пришелец-прораб.
 
Цитата
AlienSx написал:
попридержал "коней" до выяснения..
- а выяснение - вот так правильно ))
 
Ещё такой вариант.
Код
Option Explicit
'v2
Function КОЛИЧЕСТВОДЕТАЛЕЙ(изделия As Range, количество_изделий As Range, деталь As String, спецификации As Range) As Double
    Static dicSpec As Object
    If dicSpec Is Nothing Then
        Dim aSpec As Variant
        aSpec = спецификации.Resize(, 3).Value
        Set dicSpec = GetDicSpec(aSpec)
    End If
    Dim yi As Long, res As Double
    For yi = 1 To изделия.Rows.Count
        mySum res, деталь, изделия.Cells(yi, 1).Value, количество_изделий.Cells(yi, 1).Value, dicSpec
    Next
    КОЛИЧЕСТВОДЕТАЛЕЙ = res
End Function

Private Sub mySum(res As Double, ByVal resizd As String, ByVal изделие As String, количество As Double, dicSpec As Object)
    If resizd = изделие Then
        res = res + количество
    Else
        If dicSpec.Exists(изделие) Then
            Dim vv As Variant
            For Each vv In dicSpec(изделие).Keys
                mySum res, resizd, vv, количество * dicSpec(изделие)(vv), dicSpec
            Next
        End If
    End If
End Sub

Private Function GetDicSpec(aSpec As Variant) As Object
    Dim dic As Object
    Set dic = CreateObject("Scripting.Dictionary")
    
    Dim ya As Long
    For ya = 1 To UBound(aSpec, 1)
        If Not dic.Exists(aSpec(ya, 1)) Then
            Set dic(aSpec(ya, 1)) = CreateObject("Scripting.Dictionary")
        End If
        dic(aSpec(ya, 1))(aSpec(ya, 2)) = aSpec(ya, 3)
    Next
    
    Set GetDicSpec = dic
End Function

 
Цитата
ПавелW:   Можно

для примера (с тремя уровнями вложенности)
 
Оптимизированный вариант. Изменён алгоритм наполнения словаря спецификаций. Работать будет быстрее.
Код
'v3
'На изменения в диапазоне спецификаций реагирует с лагом в одну минуту. Сделано для уменьшения частоты обращения к диапазону.
Function КОЛИЧЕСТВОДЕТАЛЕЙ(изделия As Range, количество_изделий As Range, деталь As String, спецификации As Range) As Double
    Static dicSpec As Object
    Static prevTime As Date
    If dicSpec Is Nothing Or Now > prevTime + TimeSerial(0, 1, 0) Then
        Dim aSpec As Variant
        aSpec = спецификации.Resize(, 3).Value
        Set dicSpec = GetDicSpec(aSpec)
        Set dicSpec = FlatSpec(dicSpec)
        prevTime = Now
    End If
    
    Dim izd As Variant
    izd = GetArrayFromRange(изделия)
    
    Dim kol As Variant
    kol = GetArrayFromRange(количество_изделий)
    
    Dim yi As Long, res As Double
    For yi = 1 To UBound(izd)
        If dicSpec.Exists(izd(yi)) Then
            If dicSpec(izd(yi)).Exists(деталь) Then
                res = res + kol(yi) * dicSpec(izd(yi))(деталь)
            End If
        End If
    Next
    КОЛИЧЕСТВОДЕТАЛЕЙ = res
End Function

Private Function FlatSpec(dicSpec As Object) As Object
    Dim dic As Object
    Set dic = CreateObject("Scripting.Dictionary")
    Dim izdelie As Variant, izdSpec As Object
    For Each izdelie In dicSpec.Keys
        Set izdSpec = GetOneIzdSpec(izdelie, dicSpec)
        Set dic(izdelie) = izdSpec
    Next
    Set FlatSpec = dic
End Function

Private Function GetOneIzdSpec(ByVal izdelie As String, dicSpec As Object) As Object
    Dim dic As Object
    Set dic = CreateObject("Scripting.Dictionary")
    dic(izdelie) = 1
    
    Dim detal As Variant
    For Each detal In dicSpec(izdelie)
        FillTempDic dic, detal, dicSpec(izdelie)(detal), dicSpec
    Next
    Set GetOneIzdSpec = dic
End Function

Private Sub FillTempDic(dic As Object, ByVal detal As String, ByVal nDetal As Double, dicSpec As Object)
    dic(detal) = dic(detal) + nDetal
    If dicSpec.Exists(detal) Then
        Dim subdetal As Variant
        For Each subdetal In dicSpec(detal)
            FillTempDic dic, subdetal, nDetal * dicSpec(detal)(subdetal), dicSpec
        Next
    End If
End Sub

Private Function GetDicSpec(aSpec As Variant) As Object
    Dim dic As Object
    Set dic = CreateObject("Scripting.Dictionary")

    Dim ya As Long
    For ya = 1 To UBound(aSpec, 1)
        If Not IsEmpty(aSpec(ya, 1)) Then
            If IsNumeric(aSpec(ya, 3)) Then
                If aSpec(ya, 3) > 0 Then
                    If Not dic.Exists(aSpec(ya, 1)) Then
                        Set dic(aSpec(ya, 1)) = CreateObject("Scripting.Dictionary")
                    End If
                    dic(aSpec(ya, 1))(aSpec(ya, 2)) = aSpec(ya, 3)
                End If
            End If
        End If
    Next

    Set GetDicSpec = dic
End Function

Private Function GetArrayFromRange(rr As Range) As Variant
    Dim brr As Variant
    If rr.Cells.CountLarge = 1 Then
        ReDim brr(1 To 1)
        brr(1) = rr.Value
    Else
        Dim arr As Variant, ya As Long
        arr = rr.Value
        ReDim brr(1 To UBound(arr, 1))
        For ya = 1 To UBound(brr)
            brr(ya) = arr(ya, 1)
        Next
    End If
    GetArrayFromRange = brr
End Function
Страницы: 1
Читают тему
Наверх