Доброго вечера, уважаемые форумчане! Прошу вашей помощи в организации связи "один-ко-многим" между двумя таблицами через макрос. От создания сводных по нескольким таблицам (это также решило бы проблему) пока отказался, ввиду слабого знания материала и опасений "накосячить", а макрос есть макрос - из созданной им таблицы я всегда смогу отличную сводную построить. Не воспринимайте, пожалуйста, как ТЗ - выложил все мысли по теме, может чем-то поможет…
В посте №24 - рабочий макрос.
Пример такой (описание файла-примера):
есть так называемые "пироги" - типы отделки, которые представляют собой совокупность одного и более слоёв. Набор слоёв типа уникален, отличие хоть в 1 слое, или в их количестве - это уже другой тип. Так вот в 1 таблице эти самые типы расписаны по составу, а во 2 каждому помещению назначен свой тип. Напишите, пожалуйста макрос, который бы связывал эти 2 таблицы в 1 общую по ключевому полю (в примере ключевые поля выделены жёлтым). Сейчас решаю эту задачу с помощью сцепки по условию в справочнике, потом ВПРом эту сцепку в таблицу назначения, потом расцепка обратно с вставкой пустых строк - очень долго получается…
Детали по макросу (как примерно я это вижу):
1. выберите ключевое поле (диапазон) таблицы-справочника - KEY_DICT 2. выберите поле (диапазон) таблицы-справочника, значения из которого будут вставлены в таблицу назначения - LAYERS 3. выберите ключевое поле (диапазон) таблицы назначения - KEY_FILL (в таблице назначения, справа от ключевого поля создаётся столбец с именем того поля из таблицы-справочника, откуда брались LAYERS для заполнения
Комментарии:
Заполнить пустые ячейки значениями из верхних не прошу, т.к. в реальной "умной" таблице будет куча столбцов с формулами, а автозаполнить константы где нужно я всегда быстро смогу отдельным макросом. Создавать отдельный лист с новой таблицей также не нужно (вставлять в таблицу назначения) - если что, перед запуском макроса сам задублирую лист)) А пока сам попарюсь - может хоть что-то дельное напишу в коде))
Примеры похожих проблем:
Нашёл вот тут пример похожей проблемы. Решить с помощью этой надстройки не получилось - да и громоздкая она такая, основательная.
Инструкция по использованию спойлеров на форуме
1. переключиться в режим BB CODE 2. выделить код и нажать тэг кода на панели — <…> . Или просто выделить текст. 3. выделить всё, что нужно спрятать под спойлер и нажать тэг спойлера на панели — SP 4. если нужно задать имя спойлеру, то между [SPOILER и ] написать ="Текст заголовка спойлера" В режиме BB CODE должно получиться так: {/SPOILER="Заголовок спойлера"}Текст внутри спойлера{/SPOILER} Заменить фигурные скобки квадратными
Во всех делах очень полезно периодически ставить знак вопроса к тому, что вы с давних пор считали не требующим доказательств (Бертран Рассел) ►Благодарности сюда◄
SuperCat, так и знал)))) да вот как ни напишу - всё как ТЗ выглядит))))
Вот то, что смог по теме в VBA)))
Код
Sub One2Many()
Application.ScreenUpdating = False
Dim KEY_DICT As Range
Dim LAYERS As Range
Dim KEY_FILL As Range
KEY_DICT = Application.InputBox("Укажите диапазон ключевого поля словаря:", "Запрос данных", "", Type:=8)
LAYERS = Application.InputBox("Укажите диапазон значений для вставки:", "Запрос данных", "", Type:=8)
KEY_FILL = Application.InputBox("Укажите диапазон ключевого поля таблицы назначения:", "Запрос данных", "", Type:=8)
Application.ScreenUpdating = True
End Sub
Во всех делах очень полезно периодически ставить знак вопроса к тому, что вы с давних пор считали не требующим доказательств (Бертран Рассел) ►Благодарности сюда◄
Option Explicit
Option Base 1
Private Type MaterType
LCnt As Long
LNums() As Long
Maters() As String
End Type
Sub Linkuem()
Dim TList As ListObject, MTs() As MaterType, TNum, RNum, Arr(), TT(), TNums As New Collection
Dim I As Long, J As Long, K As Long, H As Long
Arr = [типы].Value
H = UBound(Arr, 1)
On Error Resume Next
For K = 1 To H
TNums.Add Arr(K, 1), CStr(Arr(K, 1))
Next
On Error GoTo 0
ReDim MTs(TNums.Count)
For Each TNum In TNums
J = J + 1
With MTs(J)
ReDim .Maters(TNums.Count), .LNums(TNums.Count)
K = 0
For I = 1 To H
If Arr(I, 1) = TNum Then
K = K + 1
.LNums(K) = Arr(I, 2)
.Maters(K) = Arr(I, 3)
End If
Next
.LCnt = K
End With
Next
Arr = [помещения].Value
H = 0
For K = 1 To UBound(Arr, 1)
TNum = TNums(Arr(K, 2))
H = H + MTs(TNum).LCnt
Next
ReDim TT(H, 3)
I = 0
For J = 1 To UBound(Arr, 1)
RNum = Arr(J, 1)
TNum = TNums(Arr(J, 2))
With MTs(TNum)
I = I + 1
TT(I, 1) = RNum
TT(I, 2) = TNum
TT(I, 3) = .LNums(1) & ". " & .Maters(1)
For K = 2 To .LCnt
I = I + 1
TT(I, 3) = .LNums(K) & ". " & .Maters(K)
Next
End With
Next
Set TList = Range("СБОР").ListObject
H = H - TList.ListRows.Count
If H Then
Application.ScreenUpdating = False
If H > 0 Then
For I = 1 To H
TList.ListRows.Add
Next
ElseIf H < 0 Then
For I = TList.ListRows.Count To TList.ListRows.Count + H + 1 Step -1
TList.ListRows(I).Delete
Next
End If
Application.ScreenUpdating = True
End If
TList.DataBodyRange.Value = TT
End Sub
С.М., наконец-то первый вариант по теме))))) спасибо вам большое - как обращаться с этим зверем?))) выделял и 1 и 2 таблицу, запускал макрос - ничего(((
Во всех делах очень полезно периодически ставить знак вопроса к тому, что вы с давних пор считали не требующим доказательств (Бертран Рассел) ►Благодарности сюда◄
Андрей VG, здравствуйте!))) спасибо за пример, но пишет "указанного файла не существует" По поводу Power Query - знаю, что так можно, но пока что боюсь DAX-формул (писал в шапке темы). Жду уроков от Николая Павлова…
Во всех делах очень полезно периодически ставить знак вопроса к тому, что вы с давних пор считали не требующим доказательств (Бертран Рассел) ►Благодарности сюда◄
Во всех делах очень полезно периодически ставить знак вопроса к тому, что вы с давних пор считали не требующим доказательств (Бертран Рассел) ►Благодарности сюда◄
чтобы не гадать на кофейной гуще, кто вам отвечает и после этого не совершать кучу ошибок - обратитесь к собеседнику на ВЫ - ответ на ваш вопрос получите - а остальное вас не касается (п.п.п. на форумах)
С.М., огромное спасибо!!! Буду разбираться с кодом))) если можно закомментируйте, пожалуйста код - хочу понять, какие переменные участвуют, чтобы запихнуть их в диалоговые окна при запуске макроса… JeyCi, благодарю за подсказку и коррекцию))) Андрей VG и Максим Зеленский, благодарою за вариант решения через PQ - в перспективе этот вариант, скорее всего станет основным, а пока буду учить матчасть))))
Во всех делах очень полезно периодически ставить знак вопроса к тому, что вы с давних пор считали не требующим доказательств (Бертран Рассел) ►Благодарности сюда◄
Максим, у меня PQL через раз ругается, что не может выполнить Join из-за одинаковых названий столбцов
Код
Table.Join(houses, {"№ типа"}, types, {"№ типа"})
, поэтому и пришлось делать "лишнюю" операцию. Причём, если соединение по двум и более столбцам, тогда без вопросов - отрабатывает как и должно быть. Один из глюков PQL - может в последних и поправили.
An Expression.Error is thrown if a column with the same name to appear in both tables of the join unless the column is selected by both key1 and key2 and the join is an inner join.
Максим Зеленский, Андрей VG, подскажите, а эти формулы только руками вбивать или есть что-то вроде мастера функций, как в Excel - тогда дело за синтаксисом и практикой))
Во всех делах очень полезно периодически ставить знак вопроса к тому, что вы с давних пор считали не требующим доказательств (Бертран Рассел) ►Благодарности сюда◄
Jack Famous написал: а эти формулы только руками вбивать или
Jack Famous вы кнопки в надстройке нажимайте и смотрите, что получается, а в расширенном редакторе - весь код целиком увидеть можно... вопрос в том в какой последовательности какие кнопки (в зависимости от того, какие шаги по коду совершить хотите)... последовательность - это уже вопрос алгоритмизации... для получения нужного на выходе... вы не стесняйтесь знакомиться с надстройкой , если чётко себе представляете алгоритм, который вам надо закодировать [решение порождает человек, простые шаги может пройти по кнопкам на ленте PQ]... сложные моменты придётся править руками, когда захотите выполнять ювелирную работу (как с макрорекодером)...- что вызвало проблему, чтобы самому прийти к такому выводу?? - особенно после прохождения по моему линку в #10 и ответа Максима там...
чтобы не гадать на кофейной гуще, кто вам отвечает и после этого не совершать кучу ошибок - обратитесь к собеседнику на ВЫ - ответ на ваш вопрос получите - а остальное вас не касается (п.п.п. на форумах)
Jack Famous написал: С.М. , Буду разбираться с кодом))) если можно закомментируйте, пожалуйста код - хочу понять, какие переменные участвуют
Вот:
Код
Private Type MaterType ' тип отделки [материалами]
Rem это (в VBA) - Пользовательский Тип Данных _
(в других языках называется Запись) - набор c именованными элементами-полями
LCnt As Long ' число слоёв в отделке
LNums() As Long ' массив номеров слоёв
Maters() As String ' массив наименований материалов в отделке
End Type
Sub Linkuem()
Dim MTs() As MaterType ' массив записей отделок
Dim TNums As New Collection ' номера (уникальные) отделок
Dim TNum, RNum ' номер_отделки и номер_слоя_в_отделке
Dim Arr() ' массив, в который считываются значения исходных таблиц
Dim TT() ' выходной массив для вставки в таблицу "СБОР"
Dim H As Long ' число строк в массивах
Dim TList As ListObject ' объект-таблица "СБОР"
Dim I As Long, J As Long, K As Long
'
JeyCi, надо пробовать - спасибо за напутствие!)) С.М., большое спасибо вам!)) Попробую что-нибудь сделать с этим наверняка куча вопросов будет
Во всех делах очень полезно периодически ставить знак вопроса к тому, что вы с давних пор считали не требующим доказательств (Бертран Рассел) ►Благодарности сюда◄
С.М., посидел, посмотрел - из переменных, которые смог: разглядеть только типы, помещения и таблица. Первые 2 - в виде именованных диапазонов, а таблица и есть таблица)) не могли бы вы изменить сам принцип работы макроса? Сейчас он решает одну конкретную задачу - ни влево, ни в право, к тому же он вставляет не значения столбца, а самостоятельно индексирует третий столбец справочника. То есть, если мне нужно вставлять какой-либо другой столбец - это очень замороченная процедура получится… Прошу вас попробовать сделать нечто похожее, как я начал в #3. То есть, выбираем ключевые поля в обоих таблицах через диалоговые окна, выбираем, какой столбец справочника переносить в целевую таблицу (размножением строк) и всё. Номеров типов и слоёв ведь тоже может не быть, поэтому макрос должен считать всё самостоятельно, ориетируясь не на числа, а только на то, сколько строк переносимого столбца справочника соответствуют одному и тому же значению ключа справочника
Во всех делах очень полезно периодически ставить знак вопроса к тому, что вы с давних пор считали не требующим доказательств (Бертран Рассел) ►Благодарности сюда◄
Доброго утра, уважаемые форумчане! АлександрС.М.согласился помочь с макросом и вот, что мы получили в итоге (выкладываю файл с макросом и отдельно модуль и класс для замечаний и предложений по оптимизации):
В модуле
Код
'Один-ко-многим по ключевому полю
'
'Заказчик: Jack Famous
'Воплотил идею в жизнь: С.М.
'Тема на форуме: http://www.planetaexcel.ru/forum/index.php?PAGE_NAME=message&FID=1&TID=84557&TITLE_SEO=84557-svyazat-2-tablitsy-v-odnu-makrosom-slozhnoe-obedinenie-dvukh-tablits&MID=705043&result=edit#message705043
'
'Присутствует проверка на соответствие ключей с перечислением несоответствий
'=======================================================================================================================================================================================================================
Option Explicit
Option Base 1
Sub One2Many()
Dim DObj1 As New Class1, FObj1 As New Class1
Dim ListObj As ListObject
Dim DRng As Range, LRng As Range, FRng As Range
Dim DArr(), LArr(), FArr(), Arr(), Item, Key
Dim H As Long, J As Long, K As Long, L As Long
Dim Txt As String, MsgRes As VbMsgBoxResult
'
Const ShowMaxLost As Long = 5 ' вывести макс. число не соответствующих ключей
Dim LostObj As Class1
'
Application.DisplayAlerts = False
On Error GoTo Exit1
Set DRng = Application.InputBox("Укажите ячейку столбца ключевого поля словаря:", "Запрос данных. Шаг1.", , Type:=8).Cells(1)
Set DRng = Intersect(DRng.EntireColumn, DRng.ListObject.DataBodyRange)
Set LRng = Application.InputBox("Укажите ячейку столбца значений словаря для вставки:", "Запрос данных. Шаг2.", , Type:=8).Cells(1)
Set LRng = Intersect(LRng.EntireColumn, LRng.ListObject.DataBodyRange)
H = DRng.Rows.Count
Set FRng = Application.InputBox("Укажите ячейку столбца ключевого поля таблицы_назначения:", "Запрос данных. Шаг3.", , Type:=8).Cells(1)
Set ListObj = FRng.ListObject
Set FRng = Intersect(FRng.EntireColumn, ListObj.DataBodyRange)
FRng.Parent.Select
DArr = DRng.Value ' ключи словаря '
LArr = LRng.Value ' значения словаря '
FArr = FRng.Value ' ключи таблицы_вставки '
'
For Each Item In FArr
If Len(Item) Then FObj1.AddItem Item
Next
Set LostObj = New Class1
For Each Item In DArr
If Len(Item) Then
If FObj1.Exists(Item) Then
DObj1.AddItem Item
Else ' ключа словаря нет в таблице_вставки
LostObj.AddItem Item
End If
End If
Next
K = LostObj.Count
If K Then
Txt = "Не все ключи словаря найдены в таблице_назначения"
GoSub Routine1
End If
'
Set LostObj = New Class1
For Each Key In FObj1.Keys
If Not DObj1.Exists(Key) Then ' ключа таблицы_вставки нет в словаре
LostObj.AddItem Key
End If
Next
K = LostObj.Count
If K Then
Txt = "Ключу в таблице_назначения нет соответствия ключей словаря"
GoSub Routine1
End If
'
For Each Key In DObj1.Keys
ReDim Arr(H)
J = 0
For K = 1 To H
If CStr(DArr(K, 1)) = Key Then
J = J + 1: Arr(J) = LArr(K, 1)
End If
Next
ReDim Preserve Arr(J)
DObj1.Item(Key) = WorksheetFunction.Transpose(Arr)
Next
'
Application.ScreenUpdating = False
ListObj.ListColumns.Add
FRng.Columns(2).EntireColumn.Insert
FRng.Cells(0, 2) = LRng.Cells(0, 1)
For K = FRng.Rows.Count To 1 Step -1
Item = FArr(K, 1)
If Len(Item) Then
If DObj1.Exists(Item) Then
Arr = DObj1.Items(CStr(Item))
H = UBound(Arr)
L = K + 1
For J = 1 To H - 1
ListObj.ListRows.Add L
Next
FRng.Cells(K, 2).Resize(H).Value = Arr
End If
End If
Next
ListObj.ListColumns(ListObj.ListColumns.Count).Delete
Application.ScreenUpdating = True
Exit Sub
Routine1:
ReDim Arr(K)
J = 0
For Each Key In LostObj.Keys
J = J + 1: Arr(J) = " " & Key
Next
If K > ShowMaxLost Then
K = ShowMaxLost + 1
ReDim Preserve Arr(K): Arr(K) = " ......."
Txt = Txt & " [" & J & "]"
End If
Txt = Txt & ":" & vbCr & Join(Arr, vbCr) & vbCr & "Продолжить выполнение ?"
If MsgBox(Txt, vbInformation + vbOKCancel + vbDefaultButton2, "Запрос данных.") = vbCancel Then Exit Sub
Return
Exit1:
Err.Clear
MsgBox "Диапазон не выбран.", , "Запрос данных."
Application.ScreenUpdating = True
End Sub
В Class1 раздела Class Modules
Код
Option Explicit
Option Base 1
Dim HisKeys As New Collection
Dim HisItems As New Collection
Dim Indexes As New Collection
'
Sub AddItem(ByVal Key As String, Optional Item = Empty)
On Error Resume Next
HisKeys.Add Key, Key
If Err = 0 Then
HisItems.Add Item, Key
Indexes.Add Indexes.Count + 1, Key
Else
Err.Clear
End If
End Sub 'AddItem'
Property Get Exists(ByVal Key As String) As Boolean
On Error Resume Next
HisKeys.Add Empty, Key
If Err Then
Err.Clear: Exists = True
Else
HisKeys.Remove Key
End If
End Property 'Exists'
Property Let Item(ByVal Key As String, NewValue)
Dim I As Long
On Error Resume Next
HisKeys.Add Empty, Key
If Err Then
Err.Clear: I = Indexes.Item(Key)
HisItems.Remove I
If I > 1 Then HisItems.Add NewValue, Key, , I - 1 Else HisItems.Add NewValue, Key, I
Else
HisKeys.Remove Key
End If
End Property 'Item'
Property Get Items()
Set Items = HisItems
End Property
Property Get Keys()
Set Keys = HisKeys
End Property
Property Get Count() As Long
Count = HisItems.Count
End Property
Инструкция к макросу
1. Для работы макроса нужно 2 "умные" таблицы, условно называемые далее "справочник" и "целевая" (макрос не привязан ни к чему) 2. в "справочнике" нужно 2 столбца: "КЛЮЧ справочника" - условное обозначения объекта, расписанного по составу и "заполнение" - непосредственно состав ключа, который мы хотим перенести в "таблицу назначения" 3. в "таблице назначения" нужен только столбец, обозначающий "КЛЮЧ назначения" - по которому, собственно и будет производиться "подтягивание" состава из "справочника" 4. макрос выдаёт 3 диалоговых окна, в которых необходимо указать только 3 ЯЧЕЙКИ трёх вышеуказанных полей (КЛЮЧ и ЗАПОЛНЕНИЕ в "справочнике" и КЛЮЧ в "таблице назначения" 5. присутствует проверка на соответствие КЛЮЧЕЙ и вывод конфликтов в MsgBox с возможностью продолжить или отменить выполнение: 5.1. не все КЛЮЧИ словаря найдены в таблице назначения 5.2. не все КЛЮЧИ таблицы назначения представлены в словаре
Во всех делах очень полезно периодически ставить знак вопроса к тому, что вы с давних пор считали не требующим доказательств (Бертран Рассел) ►Благодарности сюда◄
Отдельная благодарностьАндрей VG, Максим Зеленский и JeyCi - с их подсказок удалось довольно простым способом осуществить подобную связь в динамически обновляемом виде через запросы в Power Query - теперь, связав 2 и более таблицы через запросы по ключевым полям и удалив в этих запросах всё лишнее, можно готовый итоговый запрос выгрузить в книгу и строить сводную по нему. При изменении/дополнении/удалении данных в справочниках - всё динамически обновляется и в сводной. Теперь я серьёзно настроен на изучение Power BI в целом - спасибо, мастера!
Во всех делах очень полезно периодически ставить знак вопроса к тому, что вы с давних пор считали не требующим доказательств (Бертран Рассел) ►Благодарности сюда◄
Максим Зеленский, да - добавить в модель данных и готово))) подскажите, пожалуйста аналоги функций СУММЕСЛИМН и СЧЁТЕСЛИМН для Power Query. Или это только через DAX в Power Pivot? А то таблички посвязывал по ключевым полям (6 штук), а этого капец как не хватает…
Во всех делах очень полезно периодически ставить знак вопроса к тому, что вы с давних пор считали не требующим доказательств (Бертран Рассел) ►Благодарности сюда◄
Андрей VG, доброй ночи! Вы мне всегда подсказывали по поводу сводных и Power Pivot - надеюсь, что и тут поможете… Прикрепил файл-пример со связкой 3х таблиц через Power Query - как вышло и чего не хватает. ЖЁЛТЫМ выделены ключевые поля ЗЕЛЁНЫМ выделены поля с формулами СИРЕНЕВЫМ выделены пользовательские столбцы с формулами в Power Query ОРАНЖЕВЫМ выделено то, чего не смог достичь внутри запросов в Power Query
Во всех делах очень полезно периодически ставить знак вопроса к тому, что вы с давних пор считали не требующим доказательств (Бертран Рассел) ►Благодарности сюда◄
МОДЕРАТОРАМ: предлагаю (из-за развития темы вне названия) следующее название темы - "Объединение таблиц. Макросы, Power Query и Power Pivot"
Во всех делах очень полезно периодически ставить знак вопроса к тому, что вы с давних пор считали не требующим доказательств (Бертран Рассел) ►Благодарности сюда◄