У меня есть хранимая процедура, которая планирует работу. Выполнение этой работы занимает много времени (примерно от 30 до 40 минут). Мне нужно узнать статус этой работы. Подробности ниже помогут мне
1) Как просмотреть список всех заданий, которые запланированы на будущее, но еще не запущены
2) Как посмотреть список запущенных заданий и промежуток времени с момента их выполнения
3) Как узнать, успешно ли завершилось задание или было ли оно остановлено из-за какой-либо ошибки.


Вы не указали, как вы хотели бы видеть эти детали.
На первый взгляд предлагаю проверить Студия управления сервером.
Вы можете увидеть задания и текущее состояние в разделе «Агент SQL Server» в разделе «Задания». Если вы выбираете задание, на странице свойств отображается ссылка на журнал заданий, где вы можете увидеть время начала и окончания, наличие ошибок, шаг, вызвавший ошибку, и т. д.
Вы можете указать предупреждения и уведомления, которые будут отправляться вам по электронной почте или отправлять вам пейджинговые сообщения в случае успешного или неудачного завершения задания.
Есть монитор активности работы, но на самом деле я им никогда не пользовался. Вы можете попробовать.
Если вы хотите проверить это через T-SQL, я не знаю, как это сделать.
В нижней части окна свойств есть ссылка «Просмотреть историю заданий». Проверял на 2008 R2.
Вы можете попробовать использовать системную хранимую процедуру sp_help_job. Это возвращает информацию о работе, ее шагах, расписаниях и серверах. Например
EXEC msdb.dbo.sp_help_job @Job_name = 'Your Job Name'
Электронная документация по SQL должен содержать много информации о возвращаемых им записях.
Чтобы получить информацию о нескольких заданиях, вы можете попробовать запросить следующие системные таблицы, которые содержат различные биты информации о задании.
Их имена говорят сами за себя (кроме SysJobServers, которые содержат информацию о том, когда задание было выполнено в последний раз, и о результате).
Опять же, информацию о полях можно найти на MSDN. Например, посмотрите страницу для SysJobs
Таблица msdb.dbo.sysjobactivity необходима для фактического определения того, выполняется ли задание в данный момент. Таблица msdb.dbo.sysjobhistory, по-видимому, заполняется только после, когда задание (или шаг?) Завершается - и она вообще не заполняется для задания, запущенного «вручную» (т.е. не из расписания заданий).
Как узнать, что было фактическим стартовым шагом?
Это то, что я использую для выполнения текущих заданий (в основном, чтобы я мог убить те, которые, вероятно, зависли):
SELECT
job.Name, job.job_ID
,job.Originating_Server
,activity.run_requested_Date
,datediff(minute, activity.run_requested_Date, getdate()) AS Elapsed
FROM
msdb.dbo.sysjobs_view job
INNER JOIN msdb.dbo.sysjobactivity activity
ON (job.job_id = activity.job_id)
WHERE
run_Requested_date is not null
AND stop_execution_date is null
AND job.name like 'Your Job Prefix%'
Как сказал Тим, документация MSDN / BOL достаточно хороша в отношении содержимого таблиц sysjobsX. Просто помните, что это таблицы в MSDB.
Этот метод предпочтительнее использования хранимой процедуры msdb.dbo.sp_help_job, если эта информация должна использоваться программно (например, для написания кода T-SQL, который зависит от того, выполняется ли задание).
Было на удивление сложно узнать время выполнения текущего задания. Все найденные мной сценарии включали только время выполнения последнего запуска задания. Этот ответ - моя иголка в стоге сена.
мы можем запросить msdb разными способами, чтобы получить подробную информацию.
немногие
select job.Name, job.job_ID, job.Originating_Server,activity.run_requested_Date,
datediff(minute, activity.run_requested_Date, getdate()) as Elapsed
from msdb.dbo.sysjobs_view job
inner join msdb.dbo.sysjobactivity activity on (job.job_id = activity.job_id)
where run_Requested_date is not null
and stop_execution_date is null
and job.name like 'Your Job Prefix%'
-- Microsoft SQL Server 2008 Standard Edition:
IF EXISTS(SELECT 1
FROM msdb.dbo.sysjobs J
JOIN msdb.dbo.sysjobactivity A
ON A.job_id=J.job_id
WHERE J.name=N'Your Job Name'
AND A.run_requested_date IS NOT NULL
AND A.stop_execution_date IS NULL
)
PRINT 'The job is running!'
ELSE
PRINT 'The job is not running.'
Наш сервер sql разбился, поэтому stop_execution_date навсегда останется NULL. Так что это не решение для меня.
Я хотел бы отметить, что ни один из T-SQL на этой странице не будет работать именно потому, что ни один из них не присоединяется к таблице syssessions, чтобы получить только текущий сеанс и, следовательно, может включать ложные срабатывания.
См. Для справки: Что значит иметь вакансии с нулевой датой остановки?
Вы также можете проверить это, проанализировав процедуру sp_help_jobactivity в msdb.
Я понимаю, что это старое сообщение на SO, но я нашел это сообщение только частично полезным из-за проблемы.
SELECT
job.name,
job.job_id,
job.originating_server,
activity.run_requested_date,
DATEDIFF( SECOND, activity.run_requested_date, GETDATE() ) as Elapsed
FROM
msdb.dbo.sysjobs_view job
JOIN
msdb.dbo.sysjobactivity activity
ON
job.job_id = activity.job_id
JOIN
msdb.dbo.syssessions sess
ON
sess.session_id = activity.session_id
JOIN
(
SELECT
MAX( agent_start_date ) AS max_agent_start_date
FROM
msdb.dbo.syssessions
) sess_max
ON
sess.agent_start_date = sess_max.max_agent_start_date
WHERE
run_requested_date IS NOT NULL AND stop_execution_date IS NULL
Это действительно должно быть ответом. Есть по крайней мере еще один вопрос StackOverflow по этому поводу (здесь: stackoverflow.com/questions/18445825/…), где один из комментаторов обнаружил, что простой поиск записей sysjobactivity, где stop_execution_date имеет значение null, будет включать записи с run_requested_date, сделанные несколько недель назад, которые, очевидно, еще не выполняются. Я сам обнаружил то же самое, и когда я посмотрел на session_ids застрявших записей, я обнаружил, что они относятся к предыдущим сеансам. Вышеупомянутый запрос @efesar исключил эти исторические записи.
Я согласен. Это определенно должно быть ответом, поскольку он краток и точно сообщает вам, какие задания выполняются в настоящее время и как долго.
это должен быть ответ +1
Самый простой способ, который я нашел, - это создать хранимую процедуру. Введите "JobName" и нажмите "Перейти".
/*-----------------------------------------------------------------------------------------------------------
Document Title: usp_getJobStatus
Purpose: Finds a Current Jobs Run Status
Input Example: EXECUTE usp_getJobStatus 'MyJobName'
-------------------------------------------------------------------------------------------------------------*/
IF OBJECT_ID ( 'usp_getJobStatus','P' ) IS NOT NULL
DROP PROCEDURE usp_getJobStatus;
GO
CREATE PROCEDURE usp_getJobStatus
@JobName NVARCHAR (1000)
AS
IF OBJECT_ID('TempDB..#JobResults','U') IS NOT NULL DROP TABLE #JobResults
CREATE TABLE #JobResults ( Job_ID UNIQUEIDENTIFIER NOT NULL,
Last_Run_Date INT NOT NULL,
Last_Run_Time INT NOT NULL,
Next_Run_date INT NOT NULL,
Next_Run_Time INT NOT NULL,
Next_Run_Schedule_ID INT NOT NULL,
Requested_to_Run INT NOT NULL,
Request_Source INT NOT NULL,
Request_Source_id SYSNAME
COLLATE Database_Default NULL,
Running INT NOT NULL,
Current_Step INT NOT NULL,
Current_Retry_Attempt INT NOT NULL,
Job_State INT NOT NULL )
INSERT #JobResults
EXECUTE master.dbo.xp_sqlagent_enum_jobs 1, '';
SELECT job.name AS [Job_Name],
( SELECT MAX(CAST( STUFF(STUFF(CAST(jh.run_date AS VARCHAR),7,0,'-'),5,0,'-') + ' ' +
STUFF(STUFF(REPLACE(STR(jh.run_time,6,0),' ','0'),5,0,':'),3,0,':') AS DATETIME))
FROM msdb.dbo.sysjobs AS j
INNER JOIN msdb.dbo.sysjobhistory AS jh
ON jh.job_id = j.job_id AND jh.step_id = 0
WHERE j.[name] LIKE '%' + @JobName + '%'
GROUP BY j.[name] ) AS [Last_Completed_DateTime],
( SELECT TOP 1 start_execution_date
FROM msdb.dbo.sysjobactivity
WHERE job_id = r.job_id
ORDER BY start_execution_date DESC ) AS [Job_Start_DateTime],
CASE
WHEN r.running = 0 THEN
CASE
WHEN jobInfo.lASt_run_outcome = 0 THEN 'Failed'
WHEN jobInfo.lASt_run_outcome = 1 THEN 'Success'
WHEN jobInfo.lASt_run_outcome = 3 THEN 'Canceled'
ELSE 'Unknown'
END
WHEN r.job_state = 0 THEN 'Success'
WHEN r.job_state = 4 THEN 'Success'
WHEN r.job_state = 5 THEN 'Success'
WHEN r.job_state = 1 THEN 'In Progress'
WHEN r.job_state = 2 THEN 'In Progress'
WHEN r.job_state = 3 THEN 'In Progress'
WHEN r.job_state = 7 THEN 'In Progress'
ELSE 'Unknown' END AS [Run_Status_Description]
FROM #JobResults AS r
LEFT OUTER JOIN msdb.dbo.sysjobservers AS jobInfo
ON r.job_id = jobInfo.job_id
INNER JOIN msdb.dbo.sysjobs AS job
ON r.job_id = job.job_id
WHERE job.[enabled] = 1
AND job.name LIKE '%' + @JobName + '%'
Вышеуказанные задачи работают, но я видел много записей в msdb.dbo.sysjobactivity. где run_Requested_date не равно нулю и stop_execution_date имеет значение null ---- и задание в настоящее время не выполняется.
Я бы порекомендовал запустить следующий сценарий, чтобы удалить все фиктивные записи (убедитесь, что в данный момент не запущены никакие задания).
SQL2008:
delete activity
from msdb.dbo.sysjobs_view job
inner join msdb.dbo.sysjobactivity activity on job.job_id = activity.job_id
where
activity.run_Requested_date is not null
and activity.stop_execution_date is null
;WITH CTE_JobStatus
AS (
SELECT DISTINCT NAME AS [JobName]
,s.step_id
,s.step_name
,CASE
WHEN [Enabled] = 1
THEN 'Enabled'
ELSE 'Disabled'
END [JobStatus]
,CASE
WHEN SJH.run_status = 0
THEN 'Failed'
WHEN SJH.run_status = 1
THEN 'Succeeded'
WHEN SJH.run_status = 2
THEN 'Retry'
WHEN SJH.run_status = 3
THEN 'Cancelled'
WHEN SJH.run_status = 4
THEN 'In Progress'
ELSE 'Unknown'
END [JobOutcome]
,CONVERT(VARCHAR(8), sjh.run_date) [RunDate]
,CONVERT(VARCHAR(8), STUFF(STUFF(CONVERT(TIMESTAMP, RIGHT('000000' + CONVERT(VARCHAR(6), sjh.run_time), 6)), 3, 0, ':'), 6, 0, ':')) RunTime
,RANK() OVER (
PARTITION BY s.step_name ORDER BY sjh.run_date DESC
,sjh.run_time DESC
) AS rn
,SJH.run_status
FROM msdb..SYSJobs sj
INNER JOIN msdb..SYSJobHistory sjh ON sj.job_id = sjh.job_id
INNER JOIN msdb.dbo.sysjobsteps s ON sjh.job_id = s.job_id
AND sjh.step_id = s.step_id
WHERE (sj.NAME LIKE 'JOB NAME')
AND sjh.run_date = CONVERT(CHAR, getdate(), 112)
)
SELECT *
FROM CTE_JobStatus
WHERE rn = 1
AND run_status NOT IN (1,4)
Я столкнулся с проблемами на одном из моих серверов, запрашивающих таблицы MSDB (также известный как код, указанный выше), поскольку одно из моих заданий должно было работать, но этого не произошло. Существует системная хранимая процедура, которая возвращает статус выполнения, но нельзя выполнить инструкцию insert exec без ошибки. Внутри него находится еще одна системная хранимая процедура, которую можно использовать с оператором insert exec.
INSERT INTO #Job
EXEC master.dbo.xp_sqlagent_enum_jobs 1,dbo
И таблица для загрузки:
CREATE TABLE #Job
(job_id UNIQUEIDENTIFIER NOT NULL,
last_run_date INT NOT NULL,
last_run_time INT NOT NULL,
next_run_date INT NOT NULL,
next_run_time INT NOT NULL,
next_run_schedule_id INT NOT NULL,
requested_to_run INT NOT NULL, -- BOOL
request_source INT NOT NULL,
request_source_id sysname COLLATE database_default NULL,
running INT NOT NULL, -- BOOL
current_step INT NOT NULL,
current_retry_attempt INT NOT NULL,
job_state INT NOT NULL)
SELECT sj.name
FROM msdb..sysjobactivity aj
JOIN msdb..sysjobs sj
on sj.job_id = aj.job_id
WHERE aj.stop_execution_date IS NULL -- job hasn't stopped running
AND aj.start_execution_date IS NOT NULL -- job is currently running
AND sj.name = '<your Job Name>'
AND NOT EXISTS( -- make sure this is the most recent run
select 1
from msdb..sysjobactivity new
where new.job_id = aj.job_id
and new.start_execution_date > aj.start_execution_date ) )
print 'running'
Добро пожаловать на сайт. Взгляните на это: stackoverflow.com/help/how-to-answer и предоставьте свой код с некоторыми пояснениями
Какое дополнительное объяснение требуется, кроме того, что этот код делает именно то, о чем просил пользователь ...
Это покажет состояние / время последнего запуска или, если он запущен, покажет текущее время выполнения, номер шага / информацию и SPID (если он связан с SPID). Он также показывает включенных / отключенных и пользователей задания, где он преобразуется в формат NT SID для неразрешенных учетных записей пользователей.
CREATE TABLE #list_running_SQL_jobs
(
job_id UNIQUEIDENTIFIER NOT NULL
, last_run_date INT NOT NULL
, last_run_time INT NOT NULL
, next_run_date INT NOT NULL
, next_run_time INT NOT NULL
, next_run_schedule_id INT NOT NULL
, requested_to_run INT NOT NULL
, request_source INT NOT NULL
, request_source_id sysname NULL
, running INT NOT NULL
, current_step INT NOT NULL
, current_retry_attempt INT NOT NULL
, job_state INT NOT NULL
);
DECLARE @sqluser NVARCHAR(128)
, @is_sysadmin INT;
SELECT @is_sysadmin = ISNULL(IS_SRVROLEMEMBER(N'sysadmin'), 0);
DECLARE read_sysjobs_for_running CURSOR FOR
SELECT DISTINCT SUSER_SNAME(owner_sid)FROM msdb.dbo.sysjobs;
OPEN read_sysjobs_for_running;
FETCH NEXT FROM read_sysjobs_for_running
INTO @sqluser;
WHILE @@FETCH_STATUS = 0
BEGIN
INSERT INTO #list_running_SQL_jobs
EXECUTE master.dbo.xp_sqlagent_enum_jobs @is_sysadmin, @sqluser;
FETCH NEXT FROM read_sysjobs_for_running
INTO @sqluser;
END;
CLOSE read_sysjobs_for_running;
DEALLOCATE read_sysjobs_for_running;
SELECT j.name
, 'Enbld' = CASE j.enabled
WHEN 0
THEN 'no'
ELSE 'YES'
END
, '#Min' = DATEDIFF(MINUTE, a.start_execution_date, ISNULL(a.stop_execution_date, GETDATE()))
, 'Status' = CASE
WHEN a.start_execution_date IS NOT NULL
AND a.stop_execution_date IS NULL
THEN 'Executing'
WHEN h.run_status = 0
THEN 'FAILED'
WHEN h.run_status = 2
THEN 'Retry'
WHEN h.run_status = 3
THEN 'Canceled'
WHEN h.run_status = 4
THEN 'InProg'
WHEN h.run_status = 1
THEN 'Success'
ELSE 'Idle'
END
, r.current_step
, spid = p.session_id
, owner = ISNULL(SUSER_SNAME(j.owner_sid), 'S-' + CONVERT(NVARCHAR(12), CONVERT(BIGINT, UNICODE(LEFT(CONVERT(NVARCHAR(256), j.owner_sid), 1))) - CONVERT(BIGINT, 256) * CONVERT(BIGINT, UNICODE(LEFT(CONVERT(NVARCHAR(256), j.owner_sid), 1)) / 256)) + '-' + CONVERT(NVARCHAR(12), UNICODE(RIGHT(LEFT(CONVERT(NVARCHAR(256), j.owner_sid), 4), 1)) / 256 + CONVERT(BIGINT, NULLIF(UNICODE(LEFT(CONVERT(NVARCHAR(256), j.owner_sid), 1)) / 256, 0)) - CONVERT(BIGINT, UNICODE(LEFT(CONVERT(NVARCHAR(256), j.owner_sid), 1)) / 256)) + ISNULL('-' + CONVERT(NVARCHAR(12), CONVERT(BIGINT, UNICODE(RIGHT(LEFT(CONVERT(NVARCHAR(256), j.owner_sid), 5), 1))) + CONVERT(BIGINT, UNICODE(RIGHT(LEFT(CONVERT(NVARCHAR(256), j.owner_sid), 6), 1))) * CONVERT(BIGINT, 65536) + CONVERT(BIGINT, NULLIF(SIGN(LEN(CONVERT(NVARCHAR(256), j.owner_sid)) - 6), -1)) * 0), '') + ISNULL('-' + CONVERT(NVARCHAR(12), CONVERT(BIGINT, UNICODE(RIGHT(LEFT(CONVERT(NVARCHAR(256), j.owner_sid), 7), 1))) + CONVERT(BIGINT, UNICODE(RIGHT(LEFT(CONVERT(NVARCHAR(256), j.owner_sid), 8), 1))) * CONVERT(BIGINT, 65536) + CONVERT(BIGINT, NULLIF(SIGN(LEN(CONVERT(NVARCHAR(256), j.owner_sid)) - 8), -1)) * 0), '') + ISNULL('-' + CONVERT(NVARCHAR(12), CONVERT(BIGINT, UNICODE(RIGHT(LEFT(CONVERT(NVARCHAR(256), j.owner_sid), 9), 1))) + CONVERT(BIGINT, UNICODE(RIGHT(LEFT(CONVERT(NVARCHAR(256), j.owner_sid), 10), 1))) * CONVERT(BIGINT, 65536) + CONVERT(BIGINT, NULLIF(SIGN(LEN(CONVERT(NVARCHAR(256), j.owner_sid)) - 10), -1)) * 0), '') + ISNULL('-' + CONVERT(NVARCHAR(12), CONVERT(BIGINT, UNICODE(RIGHT(LEFT(CONVERT(NVARCHAR(256), j.owner_sid), 11), 1))) + CONVERT(BIGINT, UNICODE(RIGHT(LEFT(CONVERT(NVARCHAR(256), j.owner_sid), 12), 1))) * CONVERT(BIGINT, 65536) + CONVERT(BIGINT, NULLIF(SIGN(LEN(CONVERT(NVARCHAR(256), j.owner_sid)) - 12), -1)) * 0), '') + ISNULL('-' + CONVERT(NVARCHAR(12), CONVERT(BIGINT, UNICODE(RIGHT(LEFT(CONVERT(NVARCHAR(256), j.owner_sid), 13), 1))) + CONVERT(BIGINT, UNICODE(RIGHT(LEFT(CONVERT(NVARCHAR(256), j.owner_sid), 14), 1))) * CONVERT(BIGINT, 65536) + CONVERT(BIGINT, NULLIF(SIGN(LEN(CONVERT(NVARCHAR(256), j.owner_sid)) - 14), -1)) * 0), '')) --SHOW as NT SID when unresolved
, a.start_execution_date
, a.stop_execution_date
, t.subsystem
, t.step_name
FROM msdb.dbo.sysjobs j
LEFT OUTER JOIN (SELECT DISTINCT * FROM #list_running_SQL_jobs) r
ON j.job_id = r.job_id
LEFT OUTER JOIN msdb.dbo.sysjobactivity a
ON j.job_id = a.job_id
AND a.start_execution_date IS NOT NULL
--AND a.stop_execution_date IS NULL
AND NOT EXISTS
(
SELECT *
FROM msdb.dbo.sysjobactivity at
WHERE at.job_id = a.job_id
AND at.start_execution_date > a.start_execution_date
)
LEFT OUTER JOIN sys.dm_exec_sessions p
ON p.program_name LIKE 'SQLAgent%0x%'
AND j.job_id = SUBSTRING(SUBSTRING(p.program_name, CHARINDEX('0x', p.program_name) + 2, 32), 7, 2) + SUBSTRING(SUBSTRING(p.program_name, CHARINDEX('0x', p.program_name) + 2, 32), 5, 2) + SUBSTRING(SUBSTRING(p.program_name, CHARINDEX('0x', p.program_name) + 2, 32), 3, 2) + SUBSTRING(SUBSTRING(p.program_name, CHARINDEX('0x', p.program_name) + 2, 32), 1, 2) + '-' + SUBSTRING(SUBSTRING(p.program_name, CHARINDEX('0x', p.program_name) + 2, 32), 11, 2) + SUBSTRING(SUBSTRING(p.program_name, CHARINDEX('0x', p.program_name) + 2, 32), 9, 2) + '-' + SUBSTRING(SUBSTRING(p.program_name, CHARINDEX('0x', p.program_name) + 2, 32), 15, 2) + SUBSTRING(SUBSTRING(p.program_name, CHARINDEX('0x', p.program_name) + 2, 32), 13, 2) + '-' + SUBSTRING(SUBSTRING(p.program_name, CHARINDEX('0x', p.program_name) + 2, 32), 17, 4) + '-' + SUBSTRING(SUBSTRING(p.program_name, CHARINDEX('0x', p.program_name) + 2, 32), 21, 12)
LEFT OUTER JOIN msdb.dbo.sysjobhistory h
ON j.job_id = h.job_id
AND h.instance_id = a.job_history_id
LEFT OUTER JOIN msdb.dbo.sysjobsteps t
ON t.job_id = j.job_id
AND t.step_id = r.current_step
ORDER BY 1;
DROP TABLE #list_running_SQL_jobs;
Этот код составляет примерно 99% того, что я ищу. Думаете, есть способ добавить еще один столбец, который показывает продолжительность последнего выполнения текущего выполняемого задания? Я попытался привязать поле sysjobhistory.run_duration, но это не вернет значение, если текущий статус - «Выполняется». Спасибо, что зашли так далеко.
Это старый вопрос, но у меня была аналогичная ситуация, когда мне нужно было проверить статус заданий на SQL Server. Многие люди упомянули таблицу sysjobactivity и указали на документацию MSDN, которая великолепна. Однако я также хотел бы выделить Монитор активности работы, который предоставляет статус всех заданий, определенных на вашем сервере.
Ниже скрипт получает статус задания для каждого задания на сервере. Он также сообщает, сколько шагов есть, каков текущий шаг и затраченное время.
SELECT sj.Name,
CASE
WHEN sja.start_execution_date IS NULL THEN 'Never ran'
WHEN sja.start_execution_date IS NOT NULL AND sja.stop_execution_date IS NULL THEN 'Running'
WHEN sja.start_execution_date IS NOT NULL AND sja.stop_execution_date IS NOT NULL THEN 'Not running'
END AS 'RunStatus',
CASE WHEN sja.start_execution_date IS NOT NULL AND sja.stop_execution_date IS NULL then js.StepCount else null end As TotalNumberOfSteps,
CASE WHEN sja.start_execution_date IS NOT NULL AND sja.stop_execution_date IS NULL then ISNULL(sja.last_executed_step_id+1,js.StepCount) else null end as currentlyExecutingStep,
CASE WHEN sja.start_execution_date IS NOT NULL AND sja.stop_execution_date IS NULL then datediff(minute, sja.run_requested_date, getdate()) ELSE NULL end as ElapsedTime
FROM msdb.dbo.sysjobs sj
JOIN msdb.dbo.sysjobactivity sja
ON sj.job_id = sja.job_id
CROSS APPLY (SELECT COUNT(*) FROM msdb.dbo.sysjobsteps as js WHERE js.job_id = sj.job_id) as js(StepCount)
WHERE session_id = (
SELECT MAX(session_id) FROM msdb.dbo.sysjobactivity)
ORDER BY RunStatus desc
На странице свойств нет ссылки на историю (по крайней мере, в 2008 R2), вы, вероятно, имеете в виду контекстное меню (при щелчке правой кнопкой мыши по заданию)