I am trying to optimize `Quartz .NET` scheduler by eliminating the foreach iteration through each jobs.

It is a question related to SQL, and not in .NET.

The code iterates through each jobs to perform operations and, for each jobs, does the following:

```
bool existingJob = await JobExists(conn, newJob.Key, cancellationToken).ConfigureAwait(false);
try
{
    if (existingJob)
    {
        if (!replaceExisting)
        {
            throw new ObjectAlreadyExistsException(newJob);
        }
        await Delegate.UpdateJobDetail(conn, newJob, cancellationToken).ConfigureAwait(false);
    }
    else
    {
        await Delegate.InsertJobDetail(conn, newJob, cancellationToken).ConfigureAwait(false);
    }
}
catch (IOException e)
{
    throw new JobPersistenceException("Couldn't store job: " + e.Message, e);
}
catch (Exception e)
{
    throw new JobPersistenceException("Couldn't store job: " + e.Message, e);
}

```

Where in every await lies an sql request.

I would like to do the opposite: do a massive `JobExists` for all jobs which would then tell me which jobs exists and which don't, and then update the jobs that exists and add those who don't.

So, for example 200 000 jobs, instead of doing 200000 times exists, then add, or update, we would have 3 sql transactions, one which would validate which one exists, and then a transaction to add in bulk and a last one to update in bulk.

But I do not know how to do the massive `Exists` in SQL, I only know how to do an `IF EXISTS (SELECT A FROM B)` query for one. Is such a thing possible? Or should I do a massive `SELECT`, or a `JOIN` of some sort? How could I proceed?

**\* EDIT \***

As I have progressed the code now runs, I think, in a stored procedure like below:

```
USE [Quartz]
GO
/****** Object:  StoredProcedure [dbo].[procProcessJobs]    Script Date: 7/31/2018 10:46:09 AM ******/
SET ANSI_NULLS ON
GO
SET QUOTED_IDENTIFIER ON
GO

ALTER PROCEDURE [dbo].[procProcessJobs] 
-- Add the parameters for the stored procedure here
@Jobs JobsTableType READONLY
AS

DECLARE MYCURSOR CURSOR LOCAL STATIC READ_ONLY FORWARD_ONLY
FOR Select * FROM @Jobs

Declare @Sched NVARCHAR(120), @JobName NVARCHAR(150), @JobGroup NVARCHAR(150),
@Description NVARCHAR(250), @JobClass NVARCHAR(250), @IsDurable BIT, @IsNonConcurrent BIT,
@IsUpdateData BIT, @RequestsRecovery BIT, @JobData BIT

OPEN MYCURSOR

FETCH NEXT FROM MYCURSOR 
INTO @Sched, @JobName, @JobGroup, @Description, @JobClass, @IsDurable, @IsNonConcurrent, @IsUpdateData, @RequestsRecovery, @JobData

WHILE @@FETCH_STATUS = 0
BEGIN
    If EXISTS(SELECT * FROM QRTZ_JOB_DETAILS WHERE SCHED_NAME = @Sched AND JOB_NAME = @JobName AND JOB_GROUP = @JobGroup)
    BEGIN
        /*do your update code here*/
        UPDATE QRTZ_JOB_DETAILS 
            SET DESCRIPTION = @Description,
                JOB_CLASS_NAME = @JobClass, 
                IS_DURABLE = @IsDurable, 
                IS_NONCONCURRENT = @IsNonConcurrent, 
                IS_UPDATE_DATA = @IsUpdateData, 
                REQUESTS_RECOVERY = @RequestsRecovery, 
                JOB_DATA = @JobData  
            WHERE SCHED_NAME = @Sched AND JOB_NAME = @JobName AND JOB_GROUP = @JobGroup
    END
    ELSE BEGIN
        /*do your insert code here*/
        INSERT INTO QRTZ_JOB_DETAILS (SCHED_NAME, JOB_NAME, JOB_GROUP, DESCRIPTION, JOB_CLASS_NAME, IS_DURABLE, IS_NONCONCURRENT, IS_UPDATE_DATA, REQUESTS_RECOVERY, JOB_DATA)  
        VALUES(@Sched, @JobName, @JobGroup, @Description, @JobClass, @IsDurable, @IsNonConcurrent, @IsUpdateData, @RequestsRecovery, @JobData)
    END

END

CLOSE MYCURSOR
DEALLOCATE MYCURSOR

```

However the code takes a **long** time running and I don't know why...