Job with a cursor is abnormally slow I have a query that takes more than 9 hours to complete. 

Looking at the execution plan, I find this high-cost operator (query plan link):



How can I improve this job?
Rather than focus on the scan, try to replace the cursor with a set-based approach.

It's a bit hard to get into the details without your database schema (because I don't get any intellisense, and don't know anything about the constraints, etc), but I will outline the general approach below.  You would want to test this and make sure it's logically equivalent to your current process.

1 - Get your cursor dataset into temp table:

SELECT 
     listCt.id_traitement
    ,listCt.[N° Contrat]
    ,listct.[date_debut_contrat] as dt_effet_ct
    ,listAssu.[N° Assuré]
    ,CASE 
        WHEN listCt.is_derniere_annee = 1 AND listCt.[date_fin_contrat] &lt;= GETDATE() THEN listCt.[date_fin_contrat]
        WHEN listCt.Annee &lt; YEAR(GETDATE()) THEN CAST('31/12/' + CAST(listCt.Annee AS VARCHAR) AS DATETIME)
        WHEN listCt.Annee = YEAR(GETDATE()) AND listCt.[date_debut_contrat] &gt; GETDATE() THEN listCt.[date_debut_contrat]
        ELSE GETDATE()
    END AS date_lnk
INTO #listAssu
FROM [app_db_commercial].dbo.tmp_listing_assure AS listAssu
    INNER JOIN [app_db_commercial].[dbo].[listing_contrat_valeur] AS listCt
        ON listAssu.[id_traitement] = listCt.[id_traitement];


2 - Get your nb_adulte counts, grouped by the columns you're using later to update (id_traitement and [N° Assuré]) into a different temp table:

SELECT 
    COUNT(ac.[N° Assuré]) as nb_adulte,
    la.id_traitement,
    la.[N° Assuré]
INTO #nb_adulte_counts
FROM PRODBDD01.[app_db_mirroir].dbo.[Assurés - Contrats] AS ac
    INNER JOIN PRODBDD01.[app_db_mirroir].dbo.Assurés_Liens AS al
        ON ac.NoAssureParent = al.[N° Assuré] AND ac.[N° Assuré] = al.No_Assuré_Lien
    INNER JOIN #listAssu la
        ON ac.[N° Contrat] = la.[N° Contrat]
        AND ac.NoAssureParent = la.[N° Assuré]
WHERE 
    ac.NoAssureParent &lt;&gt; ac.[N° Assuré]
    AND ac.is_assure_principal = 0
    AND al.Type_Lien_Assuré &gt; 0 AND al.Type_Lien_Assuré &lt; 10 AND al.Type_Lien_Assuré &lt;&gt; 2
    AND la.date_lnk BETWEEN ac.[Date d'entrée] AND ISNULL([Dt_Fin], CAST('99991231' AS DATETIME))
GROUP BY 
    la.id_traitement,
    la.[N° Assuré];


3 - Do the same thing with your nb_enfant counts:

SELECT 
    COUNT(ac.[N° Assuré]) as nb_enfant,
    la.id_traitement,
    la.[N° Assuré]
INTO #nb_enfant_counts
FROM PRODBDD01.[app_db_mirroir].dbo.[Assurés - Contrats] AS ac
    INNER JOIN PRODBDD01.[app_db_mirroir].dbo.Assurés_Liens AS al
        ON ac.NoAssureParent = al.[N° Assuré] AND ac.[N° Assuré] = al.No_Assuré_Lien
    INNER JOIN #listAssu la
        ON ac.[N° Contrat] = la.[N° Contrat]
        AND ac.NoAssureParent = la.[N° Assuré]
WHERE 
    ac.NoAssureParent &lt;&gt; ac.[N° Assuré]
    AND ac.is_assure_principal = 0
    AND al.Type_Lien_Assuré = 2
    AND la.date_lnk BETWEEN ac.[Date d'entrée] AND ISNULL([Dt_Fin], CAST('99991231' AS DATETIME))
GROUP BY 
    la.id_traitement,
    la.[N° Assuré];


4 - Then you can run two set-based update statements:

UPDATE la
SET 
     nb_benef_adulte = ac.nb_adulte
FROM [app_db_commercial].dbo.tmp_listing_assure la
    INNER JOIN #nb_adulte_counts ac
        ON la.[id_traitement] = ac.[id_traitement]
        AND la.[N° Assuré] = ac.[N° Assuré];

UPDATE la
SET 
    nb_benef_enfant = ec.nb_enfant
FROM [app_db_commercial].dbo.tmp_listing_assure la
    INNER JOIN #nb_enfant_counts ec
        ON la.[id_traitement] = ec.[id_traitement]
        AND la.[N° Assuré] = ec.[N° Assuré];


I'm sure some of those steps could be combined, but this is just a quick and dirty example of how to get away from the cursor.  Despite the heavy tempdb workload here, I'd expect it to still run faster than loads of individual seeks across databases / linked servers.