Я бы очень осторожно относился к объединению дополнительных таблиц, которые могли бы дублировать строки и тем самым отбрасывать все ваши агрегатные функции. Я имею в виду следующее:
Предположим, у вас есть 2 человека, привязанных к кредиту, поэтому вы начинаете с платежа по кредиту, снова присоединяетесь к кредиту, а затем присоединяетесь к людям, которые принадлежат к этому кредиту. Теперь у вас будет 2 записи для каждого платежа по кредиту (по одной для каждого человека), с которыми вам теперь придется иметь дело.
Я бы рекомендовал использовать ВНЕШНЕЕ ПРИМЕНЕНИЕ, чтобы соединить поля с тем, что вам нужно. Если вы никогда не использовали внешнее/перекрестное применение, просто подумайте об этом как о подзапросе в операторе select:
SELECT (SELECT TOP 1 i.someColumn FROM innerTable i WHERE i.ID = t.ID) FROM someTable AS t
За исключением того, что это внутри самого соединения. Это означает, что вы можете использовать подзапрос IN SCOPE в предложении FROM для обратного присоединения. На самом деле, это, вероятно, ваш лучший выбор, чтобы все платежи были локализованы только для кредитов и чтобы все лица/группы оставались в этом другом запросе. Таким образом, ваш запрос будет выглядеть примерно так:
SELECT CR.id_credito, SUM(M.monto) AS Monto, SUM(M.interes) AS Interes, SUM(M.iva) AS IVA, SUM(M.capital) AS Capital, M.fecha_mov, ISNULL(tg.nombre, tp.nombre) Nombre
FROM movimientos AS M
JOIN cargos AS C ON C.id_movimiento = M.id_movimiento
JOIN acreditados AS A ON A.id_acreditado = M.id_acreditado
JOIN creditos AS CR ON CR.id_credito = A.id_credito
OUTER APPLY (SELECT TOP 1 G.nombre FROM grupos G JOIN agrupaciones AG on AG.id_grupo = G.id_grupo WHERE AG.id_acreditado = A.id_acreditado) tg
OUTER APPLY (SELECT TOP 1 P.nombre FROM personas P WHERE P.id_persona = A.id_persona) tp
WHERE C.status = 0
GROUP BY CR.id_credito, M.fecha_mov, ISNULL(tg.nombre, tp.nombre)
ORDER BY M.fecha_mov
Из этого следует сделать вывод, что OUTER APPLY не нужно соединять обратно с остальными таблицами в предложении FROM, потому что он уже работает в предположении, что вы запрашиваете на основе значений из остальных таблиц. источники предложения FROM (таблицы). Кроме того, не забудьте указать псевдоним для запросов APPLY (в данном случае это tg
и tp
).
Редактировать: только что заметил требование в формате древовидной структуры. Я займусь этим сейчас.
Кажется, что несмотря ни на что, вы хотите, чтобы ВСЕ записи для каждого человека также были возвращены. В этой ситуации вы хотите, чтобы одна и та же запись возвращалась для всех людей в группе. На мой взгляд, лучший способ сделать это — присоединиться к таблице лиц как обычное соединение (вместо применения), а затем просто сгруппировать по полям tg.nombre
и personas.nombre
. Таким образом, вы по-прежнему получаете каждого человека, привязанного к кредиту, но также возвращаете имя группы, чтобы, если есть группа, вы могли использовать ее в качестве корневого узла в дереве.
Что касается фактического создания этого в виде дерева, вам придется проделать дополнительную работу, чтобы на самом деле настроить его таким образом. Я бы предложил получить запрос в пригодном для использования формате и скомпилировать его вместе с помощью группировки LINQ.
Затем запрос нужно будет изменить следующим образом: измените строку OUTER APPLY (SELECT TOP 1 P.nombre FROM personas P WHERE P.id_persona = A.id_persona) tp
в приведенном выше изменении на INNER JOIN personas P on P.id_persona = A.id_persona
, измените выбор/группировку с ISNULL(tg.nombre, tp.nombre) Nombre
на tg.nombre Grupo, P.nombre Nombre
. В своем коде после получения результатов запроса используйте следующую инструкцию LINQ, чтобы получить запрос (при условии, что это как минимум .NET 3.5):
// Assume the query results are in dtLoanPayments strongly typed datatable.
// Also assume we have a TreeView called tvLoans
dtLoanPayments.AsEnumerable().GroupBy(tr => tr.Grupo).ToList().ForEach(tr =>
{
(tr.Key == null ? tvLoans.Nodes : tvLoans.Nodes.Add(tr.Key)).AddRange
(tr.Select(ti => { TreeNode tempNode = new TreeNode(ti.Nombre); tempNode.Tag = ti; return tempNode; }).ToArray());
});
Опять же, это действительно зависит от того, как вы хотите, чтобы они отображались. Если вам нужна дальнейшая группировка по человеку, вам нужно будет сделать еще один GroupBy перед внутренним tr.Select
, чтобы это было tr.GroupBy(ti => ti.Personas).Select
, и вам нужно будет создать еще один TreeNode для каждой записи платежа по кредиту. В таких ситуациях просто помните, что обычно намного проще разделить логику отображения и данных. Что касается запроса, просто убедитесь, что данные, которые вы возвращаете, верны, а затем займитесь форматированием в коде вашего приложения.
Редактировать: Чтобы ответить на ваш вопрос в комментариях, добавьте еще немного кода. Это всего лишь вопрос преобразования вашего списка элементов в правильно вложенные объекты TreeNode. Я думаю, что мое объяснение выше как бы отвечает на него, но я могу дать вам код с пояснениями того, что именно происходит. Предполагая, что ваш список называется amortList
, а ваш TreeView называется tvLoans
, попробуйте следующее:
// First, group by group name. If it's null (meaning no group), then it will just be
// the person's name. Doing ToList() after grouping so we can do a ForEach.
amortList.GroupBy(tr => string.IsNullOrEmpty(tr.Grupo) ? tvLoans : tvLoans.Add(tr.Grupo)).ToList()
.ForEach(tr =>
// This gives us the node we'll be adding each record to as the key. Now all
// we need to do is add all loan records to the node, grouping by person
{
tr.Key.Nodes.AddRange(
// Group by person's name first
tr.GroupBy(ti => new TreeNode(ti.Nombre))
// Then transform into each node (loan payment, etc) and add to the person node.
.Select(ti =>
{
ti.Key.Nodes.AddRange(
ti.Select(tn => new TreeNode(/*Use whatever field here to display.*/).ToArray());
return ti.Key; // Return TreeNode.
}).ToArray());
}
Если это все еще сбивает с толку, вы можете сделать foreach, который выполняет то же самое (и его легче понять).
foreach(_Amortizacion amNode in amortList)
{
// Using Null Coalescing to create the node if it doesn't exist.
// First check if group is null. If it isn't, try to pull the node and create it if it doesn't exist.
TreeNodeCollection workingNode = !string.IsNullOrEmpty(amNode.Grupo) ?
(tvLoans.Nodes[amNode.Grupo] ?? tvLoans.Nodes.Add(amNode.Grupo)).Nodes :
tvLoans.Nodes;
// At this point, workingNode is the NodeCollection we'll be adding the person to. Creating if doesn't exist.
workingNode = (workingNode[amNode.Personas] ?? workingNode.Add(amNode.Personas)).Nodes;
// Now we're adding the actual loan record to the person Node.
workingNode.Add(new TreeNode(/* This is what will show up in TreeView. */) { Tag = amNode, Name = /* Specify a key here, in case you want to search. */ });
}
person
SPFiredrake
schedule
17.01.2012