Поля корреляции ошибок - PullRequest
0 голосов
/ 02 апреля 2012

Я получаю сообщение об ошибке, когда я делаю следующее:

SELECT *
from cGift c1 left join
    (select c2.gidnumb, "t" new from cGift c2 where c2.gidnumb = 
        (select c3.gidnumb from cgift c3 where c3.id = c2.id and c3.date =
            (select min(c4.date) from cgift c4 where c2.id == c4.id ))) tNew
    on tNew.gidnumb = c1.gidnumb

В основном у меня есть таблица cgift со списком пожертвований.Мне нужен запрос, который возвращает cgift с дополнительным столбцом, содержащим либо «т» или ноль.Первым пожертвованием (cgift.date) каждого донора (cgift.id) должно быть «t», остальное - ноль.

Пример:

gidnumb..id....date......new
10.......1.....2/1/2010..null
11.......2.....1/1/2010..t
12.......3.....1/1/2010..t
13.......1.....3/1/2010..null
14.......2.....2/1/2010..null
15.......4.....1/1/2010..t
16.......1.....1/1/2010..t

Нули могут быть бланками или fили wtvr.

Может кто-нибудь сказать мне, что не так с моим запросом, это сводит меня с ума.

Ответы [ 3 ]

2 голосов
/ 02 апреля 2012

Я думаю, что следующее должно работать почти во всех продуктах SQL:

SELECT
    cGift.gidnumb,
    cGift.id,
    cGift.date,
    first.isfirst
FROM cGift
LEFT JOIN (
    SELECT id, MIN(date) AS date, 't' AS isfirst
    FROM cGift
    GROUP BY id
) first ON cGift.id = first.id AND cGift.date = first.date

ОБНОВЛЕНИЕ (с учетом дополнительных критериев):

Если человек может сделать пожертвование более чем один раз на MIN(date), и вы хотите, чтобы только одно пожертвование было отмечено t, вы можете, например, сделать это:

SELECT
    cGift.gidnumb,
    cGift.id,
    cGift.date,
    first.isfirst
FROM cGift
LEFT JOIN (
    SELECT
        MIN(g.gidnumb) AS g.gidnumb,
        g.id,
        g.date,
        't' AS isfirst
    FROM cGift g
    INNER JOIN (
        SELECT id, MIN(date) AS date
        FROM cGift
        GROUP BY id
    ) f ON g.id = f.id AND g.date = f.date
    GROUP BY g.id, g.date
) first ON cGift.id = first.id AND cGift.date = first.date

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

Этот запрос должен по-прежнему выполняться в любой СУБД. Это может быть менее эффективным, учитывая двойную группировку. Вот альтернатива, которая также использует только стандартный SQL, без специфических для поставщика функций (он также должен быть немного более гибким, чем предыдущий запрос):

SELECT
    gidnumb,
    id,
    date,
    CASE
        WHEN NOT EXISTS (
            SELECT *
            FROM cGift
            WHERE id = g.id
                AND (
                    date < g.date OR
                    date = g.date AND gidnumb < g.gidnumb
                )
        )
        THEN 't'
    END AS isfirst
FROM cGift g

Как видите, столбец isfirst вычисляется с помощью самопроверки таблицы: если в этой таблице нет строки с таким же id и более ранней датой или, если дата совпадает, меньше gidnumb, эта строка должна быть помечена как t. При отсутствии ELSE часть CASE подразумевается ELSE NULL. Вы можете, если хотите, добавить что-то вроде ELSE 'f'.

Тем не менее, ваш продукт SQL может обладать функциями, которые вы могли бы получить, создав, возможно, более простой и эффективный запрос. Некоторые продукты, например, поддерживают функции ранжирования (которые уже являются частью стандарта SQL, просто они еще не поддерживаются повсеместно), и вот что вы можете сделать с помощью функции ранжирования ROW_NUMBER():

SELECT
    gidnumb,
    id,
    date,
    CASE ROW_NUMBER() OVER (PARTITION BY id ORDER BY date, gidnumb)
        WHEN 1
        THEN 't'
    END AS isfirst
FROM cGift g

Этот запрос ранжирует строки, разделяя их по id и сортируя сначала по date, затем по gidnumb. Каждая строка с рейтингом 1 в этом случае становится той, которую следует отличать с t.

1 голос
/ 02 апреля 2012

Я не вижу ошибок в вашем запросе.

Но я рекомендую немного изменить ваш запрос.

Это вернет первое пожертвование для каждого идентификатора

SELECT id, gidnymb, date, "t" As new_column
FROM cGift
GROUP BY id
HAVING date = MIN(date);

Это вернет следующие пожертвования

SELECT id, gidnymb, date, null AS new_column
FROM cGift
LEFT JOIN (
    SELECT id, gidnymb, date, "t" As new_column
    FROM cGift
    GROUP BY id
    HAVING date = MIN(date)
) USING(id)
WHERE  new_column IS NULL

Теперь объедините это с помощью UNION.

(SELECT id, gidnymb, date, "t" As new_column
FROM cGift
GROUP BY id
HAVING date = MIN(date))

UNION

(SELECT id, gidnymb, date, null AS new_column
FROM cGift
LEFT JOIN (
    SELECT id, gidnymb, date, "t" As new_column
    FROM cGift
    GROUP BY id
    HAVING date = MIN(date)
) USING(id)
WHERE  new_column IS NULL)

Я не проверял это, но у вас есть идея.

0 голосов
/ 02 апреля 2012

В SQL Server 2005+ вы можете сделать это

SELECT
    gidnumb,
    id,
    Date,
    CASE RANK() OVER (PARTITION BY id ORDER BY Date)
        WHEN 1 Then 't'
        ELSE Null
    END
FROM
    cGift
...