Как решать, что субконсультация не возвратила более одной стоимости?

Я - студент бега вычислительных систем, и в моем классе управления базой данных они просили меня, чтобы реализовывать trigger, который обновлял конечную квалификацию, автоматически обновив какую-то из частичных квалификаций. Моя база данных - следующая:

create table alumno
(Id_A  smallint not null identity(1,1) constraint id_A_pk primary key,
Nombre_alum  char(20), App_Alum  char(20), Apm_Alum  char(20),  
edad_alum smallint, sexo_alum char(1), ID_c smallint, 
id_gpo smallint, fec_nac datetime)
go

create table Materia
(ID_M smallint not null constraint ID_M_pk primary key,
Nombre_Materia char (35), id_c smallint,cuatrimestre tinyint)
go

create table Calificacion
(Id_A  smallint constraint calificacion_id_A_FK references alumno(Id_A), 
ID_M smallint constraint calificacion_id_M_FK references materia(ID_M),
calif_1 decimal, calif_2 decimal, 
calif_3 decimal, calif_4 decimal, 
calif_f decimal, ano smallint, Periodo tinyint)
go

create table Carrera
(ID_c smallint identity(100,10) constraint id_c_PK primary key,
Nombre_carrera char(30))
go

create table grupo
(id_gpo smallint not null identity(1,1) constraint ID_g_Pk primary key,
Nombre_gpo char(6))

create table profesor
(Id_p smallint identity(1,1) constraint profesor_id_p_Pk Primary key, 
nombre_prof char(20), app_prof char(20), apm_prof char(20),
edad_prof tinyint, sexo_prof char(1), Id_jefe smallint,
id_c smallint constraint profesor_id_c_FK references carrera(id_c))
go

create table prof_gpo
(Id_gpo smallint constraint Prof_gpo_id_gpo_fk references grupo(id_gpo), 
id_p smallint constraint Prof_gpo_id_p_fk references profesor(id_p),
aula char(4))
go

alter table alumno
add constraint alumno_ID_c_FK foreign key (id_c)
references carrera(id_c)
go

alter table alumno
add constraint alumno_ID_gpo_FK foreign key (id_gpo)
references grupo(id_gpo)
go

alter table materia
add constraint materia_id_c_Fk foreign key(id_c) references carrera(id_c)
go

alter table profesor
add constraint profesor_Id_jefe_FK foreign key(Id_jefe) 
references profesor(Id_p)
go

И мой стрелок - следующий:

Alter trigger tg_promAlu
On Calificacion
After Update
    As
        Begin
            Declare @cveAlu smallint = (Select Id_A from inserted)
            Declare @cveMat smallint = (Select ID_M from inserted)
            Declare @prom decimal = (Select AVG(calif_1+calif_2+calif_3+calif_4)
                                                            From Calificacion Where Id_A = @cveAlu and ID_M = @cveMat)
            Where Id_A = @cveAlu and ID_M = @cveMat

            Declare @promPrint varchar(5) = convert(varchar(5),@prom)
            Print 'El promedio del alumno es: ' + @promPrint
        End
Go

И ошибка, которую он показывает мне, - следующая:

Mens 512, Уровень 16, государство 1, Процедура tg_promAlu, Линия 6 Субконсультация возвратил более одной стоимости, что не правильно, когда он идет далее =! =, <<=,>,> =, или когда он используется как выражение. Закончилась инструкция.

3
задан 06.01.2017, 04:47
0 ответов

Легко упорядочивать это, только ты должен использовать top 1, но нужно видеть, есть ли у тебя правильные фильтры.

Например, я попробовал бы менять эту консультацию:

Declare @cveAlu smallint = (Select Id_A from inserted)

Из-за чего-то как это:

Declare @cveAlu smallint = (Select top 1 Id_A from inserted)

Таким образом только тебя возвратит реестр. Сейчас, если ты хочешь последнего, я предполагаю, что ты мог бы упорядочивать это нисходящей формы:

Declare @cveAlu smallint = (Select top 1 Id_A from inserted order by Id_A desc)

Так ты мог бы менять каждую суб-консультацию убеждаясь в том, что только я возвратил стоимость уже с top 1 или улучшая твои фильтры.

2
ответ дан 03.12.2019, 17:52
  • 1
    Большое спасибо, солитеры razó n. его был достаточным, хотя top 1 добавил в суб-консультациях. –  05.01.2017, 09:36
  • 2
    Но ты должен санкционировать, что informació n был правильным, смоги быть ошибкой ДАННЫХ..., чтобы гарантировать тебя, что informació n estará обновляя правильно. Привет. –  05.01.2017, 09:42

Хотя вводить один TOP 1, действительно, удали ошибку, которую ты получаешь, это просто решает симптом игнорируя истинную проблему. Я уверен, что это решение не приведено в соответствие в этом случае, и будет вызывать у тебя проблемы с твоими данными.

Какова истинная проблема?

Первое, важно понимать почему recibistes ошибку. Ошибка происходит, потому что временная таблица inserted он содержит более одного реестра. Это происходит, когда ты выполняешь решение update что затрагивает более одного реестра одновременно в таблице Calificacion.

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

Заметь то, что говорит ссылку следующего MSDN по этому поводу: Создавать desencadenadores DML, чтобы управлять несколькими линиями данных:

Когда он напишет код для desencadenador DML, будет иметь в виду, что инструкция, которая способствует тому, чтобы был включен стрелок, может быть единственная инструкция, которая касалась бы нескольких линий данных, вместо единственной линии. Это поведение обычное для desencadenadores UPDATE и DELETE, так как эти инструкции обычно касаются нескольких линий.

Это твой недостаток. Твой стрелок понимает, что ты только будешь манипулировать единственным реестром одновременно. И использовать TOP 1 только стоит тебе прятать реальность и создавать иллюзию, которой еще ты можешь манипулировать реестром одновременно. Но, делая это, ты игнорируешь реестры, которыми ты был бы должен манипулировать.

Другие проблемы

  1. Деталь в меньшем, но важном внешнем виде, чтобы мочь разрабатывать спусковой механизм мульти-реестры правильно: нуждаться в том, чтобы определить первичный ключ в твоей таблице Calificacion. Я не вижу, что у тебя была одна. Я прихожу к заключению о том, что предполагается, что комбинация Id_A, Id_M она единственная, но ты это не определил ясно.

  2. Другая проблема состоит в том, что ты не используешь функцию AVG() правильно. Сделав:

    Select AVG(calif_1+calif_2+calif_3+calif_4)
    

    ... кажется, что ты под печатью, что это будет давать тебе среднюю величину 4 квалификаций. Это случай абсолютно. AVG(expresión) он возвращает тебе среднюю величину выражения между несколькими реестрами. Но поскольку ты только манипулируешь единственным реестром, в действительности только ты складываешь 4 квалификации, не делая средней величины. Но что ты увидел проблему, здесь я оставляю тебе ссылку для того, чтобы ты увидел результат выполнения select avg(100 + 100 + 100 + 100).

  3. Если идея состоит в том, чтобы держать в курсе поле calif_f автоматически, было бы хорошим, что trigger стреляет также с решениями INSERT.

Решение

Принимая во внимание намерение твоего trigger:

они просили меня, чтобы реализовывать trigger, который обновлял конечную квалификацию, автоматически обновив какую-то из частичных квалификаций.

... я наладил бы следующие точки:

  1. Устанавливать Id_A, ID_M как первичный ключ таблицы Calificacion (если бы первичный ключ был должен определяться с другими колоннами, это не проблема, но требует, чтобы приспособилось условие в join trigger, видеть ниже).
  2. Изменять trigger для того, чтобы он выстрелил с INSERTs также
  3. Исправлять рисунок trigger для того, чтобы он манипулировал несколькими реестрами правильно. Этого добиваются делая один join с временной таблицей inserted.
  4. Исправлять вычисление средней величины. Чтобы это делать правильно, и манипулируя NULL стандартной формы, решение осложняется немного с выражениями CASE, но это правильный способ это делать, не используя функцию AVG().
  5. Оптимизация: добавлять условие, которое только обновляет среднюю величину, если по крайней мере одно из полей calif_N он был изменен. Это также стоит защитой для того, чтобы предотвратить перекурсивные выстрелы, если сервер конфигурировался, чтобы позволять их.

Исправленный trigger был бы:

create trigger tg_promAlu
On Calificacion
After insert, Update
As
Begin
    set nocount on;

    if update(calif_1)
         or update(calif_2)
         or update(calif_3)
         or update(calif_4)
    update c
       set c.calif_f = case when i.calif_1 is not null
                              or i.calif_2 is not null
                              or i.calif_3 is not null
                              or i.calif_4 is not null
                            then
                              (coalesce(i.calif_1, 0)
                              + coalesce(i.calif_2, 0)
                              + coalesce(i.calif_3, 0)
                              + coalesce(i.calif_4, 0))
                              /
                              (case when i.calif_1 is null then 0 else 1 end
                               + case when i.calif_2 is null then 0 else 1 end
                               + case when i.calif_3 is null then 0 else 1 end
                               + case when i.calif_4 is null then 0 else 1 end)
                            end
      from inserted i
      join Calificacion c
        on c.Id_A = i.Id_A
       and c.ID_M = i.ID_M;
End;
Go

Если ты хочешь увидеть trigger в действии: Demo

0
ответ дан 03.12.2019, 17:52