Помогите создать запрос (конкатенация текстовых полей)

fatherboard
Дата: 05.03.2008 16:54:25
Есть база учета обращений клиентов (таб. Обращения). При обращении клиент может запросить информацию о нескольких объектах (таб. Объекты спроса) и ему могут предложить несколько объектов (таб. Объекты предложения). Есть так же справочник объектов, в котором содержатся названия.
Необходимо создать запрос, результатом которого была бы таблица:

idid менеджера id клиента дата время Объекты спроса Объекты предложения
1 58 285931 26.04.2007 10:00:00 у метро;Приморский СМ348;Восток
2 9 242875 17.11.2006 10:00:00 везде СМ348;Юг46
и т.д...

Пока в голову приходит сделать только через дополнительные таблицы.
Можно ли одним запросом? Или хотя бы многими запросами, но только без временных таблиц.

База прилагается...
adv
Дата: 05.03.2008 17:08:19
похожее было недавно.
fatherboard
Дата: 05.03.2008 19:44:00
В результате запроса получается примерно 50000 записей. Если тип запроса "выборка", то отрабатывает быстро, а вот если "на добавление", то виснет наглухо. Ждал минут 10 - потом даже Ctrl+Break не работал.
Можно ускорить это дело?
Текст запроса
INSERT INTO tmp_Поиск ( Менеджер, Клиент, Дата, Время, Спрос, Предложение, Мотивы )
SELECT DISTINCTROW (Trim(Менеджеры.Фамилия) & " " & Trim(Менеджеры.Имя)) AS Менеджер, 
(Trim(Клиенты.Фамилия) & " " & Trim(Клиенты.Имя) & " " & Trim(Клиенты.Отчество)) AS Клиент, 
Обращения.Дата, 
Обращения.Время, 
(MySelect("запрос справочник объектов спроса","объект","[id обращения]=" & обращения.id)) AS Спрос, 
(MySelect("запрос справочник объектов предложения","Объект","[id обращения]=" & обращения.id)) AS Предложение, 
(MySelect("запрос мотивы покупки все","мотив","[id обращения]=" & обращения.id)) AS Мотивы
FROM [Справочник объектов] 
INNER JOIN (((Менеджеры RIGHT JOIN (Клиенты RIGHT JOIN Обращения ON Клиенты.ID = Обращения.[ID клиента]) ON Менеджеры.ID = Обращения.[ID менеджера]) 
INNER JOIN [Объекты предложения] ON Обращения.ID = [Объекты предложения].[ID обращения]) 
INNER JOIN [Объекты спроса] ON Обращения.ID = [Объекты спроса].[ID обращения]) ON ([Справочник объектов].ID = [Объекты спроса].[Код объекта]) 
AND ([Справочник объектов].ID = [Объекты предложения].[Код объекта]);
fatherboard
Дата: 05.03.2008 19:50:38
Это текст публичной функции MySelect которую в запросе использую
Public Function MySelect(TableName As String, PoleName As String, WhereTxt As String) As String
Dim rec As DAO.Recordset, result As String
Set rec = CurrentDb.OpenRecordset("Select [" & PoleName & "] From [" & TableName & "] Where " & WhereTxt & ";")
Do Until rec.EOF
    result = result & Trim(rec(PoleName)) & "; "
    rec.MoveNext
Loop
rec.Close
Set rec = Nothing
If result <> "" Then result = left(result, Len(result) - 2)
MySelect = result
End Function
Karfaqen
Дата: 05.03.2008 19:57:18
Ну так 50,000 раз по три рекордсета открывать - это бюст на родине героя как минимум :)
Раз вы все равно делаете через временную таблицу, так и сделайте уже все на VB.
fatherboard
Дата: 05.03.2008 20:01:47
Karfaqen
Раз вы все равно делаете через временную таблицу, так и сделайте уже все на VB.

Всмысле вставить текст запроса в DoCmd.RunSQL ("")?
Это ускорит?
Karfaqen
Дата: 05.03.2008 20:20:06
fatherboard
Всмысле вставить текст запроса в DoCmd.RunSQL ("")?
Нет, речь не о том, конечно.

Например можно так. Сначала этим запросом добавьте все записи (+исходный уникальный id в список полей добавьте), но при этом во все поля конкатенации пишите NULL. Это будет быстро.

Потом откройте четыре Recordset'а, сортированные по этому полю id: первый - этот самый получившися результат, и еще три - на основе тех запросов, что вы указывали в вызовах функции MySelect.

Далее, перемещаясь в цикле по записям первого рекордсета, собираете в ТРИ разных строки значения из записей трех подчиненных рекордсетов - ПОКА их id совпадает с id записи первого рекордсета.

Когда совпадения закончились, записываете накопленный результат в соответсвующие поля первого рекордсета, и переходите к следующей записи.
Karfaqen
Дата: 05.03.2008 20:45:51
Вдогонку. Кстати, глядя на ваш запрос, имею сильное подозрение, что он у вас неадекватен вашей цели.

Вы же хотите, чтобы у вас в результирующем наборе были записи ОБРАЩЕНИЙ (одно обращение - одна запись), и у них в особых полях, через запятую собраны значения из нескольких подчиненных (для каждого обращения) записей.

Однако, я вижу в этом запросе имена таблиц "Объекты предложения", "Объекты спроса" - которые смахивают на имена как раз тех таблиц, значения которых вы хотите собтирать в строку.

Если это на самом деле так, то вы должны понять, что участие этих подчиненных таблиц в запросе даст вам совсем иной результат по составу записей - в итоге вы ведь получаете частичное перемножение числа записей этих таблиц.

Вот у вас и
fatherboard
В результате запроса получается примерно 50000 записей
А их. по идее должно ведь быть столько, сколько было обращений. ИМХО, вы в первую очередь пересмотрите запрос - добейтесь чтобы (SELECT) давал вам ТОТ набор записей, что нужно. Если у вас после этого записей там станет раз в десять меньше, то и вариант с вызовом MySelect станет приемлем.
fatherboard
Дата: 06.03.2008 10:41:22
Karfaqen
Вот у вас и
fatherboard
В результате запроса получается примерно 50000 записей
А их. по идее должно ведь быть столько, сколько было обращений.

Вы абсолютно правы, эти таблицы остались от моих неудачных попыток и совсем там не нужны. Но вот только колличество обращений более 70000, что сейчас и показывает (правильно показывает! :)) запрос.
Так что не подойдет вариант с функцией, сейчас попробую вариант из Вашего предыдушего поста.
fatherboard
Дата: 06.03.2008 12:40:51
Уважаемый Karfaqen, если я правильно понял идею, то код должен быть примерно такой:
sqlstr = "SELECT Обращения.ID FROM Обращения ORDER BY Обращения.ID"
Set r1 = CurrentDb.OpenRecordset(sqlstr)
Set r2 = CurrentDb.OpenRecordset("Запрос справочник объектов спроса")
Set r3 = CurrentDb.OpenRecordset("Запрос справочник объектов предложения")
Set r4 = CurrentDb.OpenRecordset("Запрос мотивы покупки все")
Do Until r1.EOF
    
    Do Until r2.EOF
        If r2.Fields(0) = r1.Fields(0) Then os = os & r2.Fields(1) & "; "
        r2.MoveNext
    Loop
    If os <> "" Then
        os = left(os, Len(os) - 2)
        DoCmd.RunSQL ("UPDATE tmp_Поиск SET tmp_Поиск.Спрос='" & Trim(os) & "' WHERE tmp_Поиск.[id обращения] =" & r1.Fields(0) & ";")
        os = ""
    End If
    
    'r3
    'r4

r1.MoveNext
Loop
Только вот почему-то в результате работы цикла изменяется только запись с ID = 1, а все остальные остаются пустые. Не могу понять почему так происходит, не вижу ошибки..