J'essaie d'améliorer un ancien code qui fonctionne actuellement avec loop et met à jour deux tables différentes. Voici un exemple de code:
<cfquery name="findStores" datasource="test">
SELECT store_id, year_id, start_dt, end_dt
FROM stores
WHERE store_id= #storeID#
AND year_id = #yearID#
AND store_type_id IN (5,6,7,8,9,10)
</cfquery>
<cfloop query="findSubAgencies">
<cfscript>
storeID= store_id;
yearID = year_id;
startDt = start_dt;
endDt = end_dt;
</cfscript>
<cfquery name="updateStatus" datasource="test">
UPDATE store_status
SET start_date = '#start_dt#',
end_date = '#end_dt#',
last_update_date = getDate()
WHERE status_id = (
SELECT status_id
FROM store_status
WHERE store_id = #storeID#
AND year_id = #yearID#
)
</cfquery>
<cfquery name="updateStoreDoc" datasource="test">
UPDATE store_doc
SET approve_start_date = '#start_dt#',
approve_end_date = '#end_dt#',
status_id = (SELECT max(status_id) FROM store_status WHERE store_id = #storeID# AND year_id = #yearID#)
WHERE store_id = #storeID#
AND year_id = #yearID#
</cfquery>
</cfloop>
Le code ci-dessus boucle sur la requête findStores et met à jour deux tables différentes. Je me demandais si tout cela pouvait être fait dans Sybase SQL? Au lieu de boucler sur la requête et d'avoir deux requêtes internes supplémentaires, pouvons-nous simplement utiliser une table temporaire ou WHERE IN () pour obtenir le même résultat? Si quelqu'un a des suggestions, faites-le moi savoir.
3 Réponses :
Voici l'idée générale (pour les identifiants explicites):
UPDATE store_status dest
SET start_date = src.start_dt,
end_date = src.end_dt,
last_update_date = getDate()
FROM stores src
WHERE dest.store_id = src.store_id
AND dest.year_id = src.year_id
AND store_type_id IN (5,6,7,8,9,10)
go
UPDATE store_doc dest
SET approve_start_date = src.start_dt,
approve_end_date = src.end_dt,
status_id = max_status_id
FROM (SELECT max(status_id) max_status_id, max(start_date) start_dt,
max(end_date) end_dt, store_id, year_id FROM store_status
GROUP BY store_id, year_id) src
WHERE dest.store_id = src.store_id
AND dest.year_id = src.year_id
go
Parcourir les magasins:
declare
@storeID int, @yearID int
begin
set @storeID = 1
set @yearID = 2019
UPDATE store_status dest
SET start_date = src.start_dt,
end_date = src.end_dt,
last_update_date = getDate()
FROM stores src
WHERE dest.store_id = src.store_id
AND dest.year_id = src.year_id
AND src.store_id=@storeID
AND src.year_id=@yearID
end
go
Je n'ai pas vu ColdFusion depuis un moment ... cette balise CFQUERY me rappelle de bons souvenirs :)
Si vous exécutez la même requête UPDATE pour un groupe d'enregistrements, vous devez exécuter une seule requête par lots (comme vous l'avez suggéré). Pour ce faire pour tous les magasins, vous pouvez utiliser une variante de votre requête findStores comme source de vos UPDATE , quelque chose comme ceci:
<!--- Update status --->
<cfquery name="updateStatus" datasource="test">
UPDATE store_status tgt
FROM (
-- findStores query
SELECT store_id, year_id, start_dt, end_dt
FROM stores
WHERE store_type_id IN (5,6,7,8,9,10)
) src
SET start_date = src.start_dt,
end_date = src.end_dt,
last_update_date = getDate()
WHERE status_id = (
SELECT status_id
FROM store_status
WHERE store_id = src.store_id
AND year_id = src.year_id
)
</cfquery>
<!--- Update store info --->
<cfquery name="updateStoreDoc" datasource="test">
UPDATE store_doc tgt
FROM (
-- findStores query with MAX(status) calculation
SELECT
st.store_id, st.year_id, st.start_dt, st.end_dt,
MAX(stt.status_id) AS status_id_max
FROM stores st
LEFT JOIN store_status stt ON st.store_id = stt.store_id -- Get status info
AND st.year_id = stt.year_id
WHERE st.store_type_id IN (5,6,7,8,9,10)
GROUP BY 1,2,3,4
) src
SET approve_start_date = src.start_dt,
approve_end_date = src.end_dt,
status_id = src.status_id_max
WHERE store_id = src.store_id
AND year_id = src.year_id
</cfquery>
Je ne sais pas si votre SGBD sous-jacent prend en charge cette syntaxe et comment Cold Fusion la gérera, il faudra donc peut-être quelques bricolages pour le faire fonctionner.
De plus, qu'essayez-vous de faire exactement dans la requête updateStatus ? Vous faites référence à status_id dans votre clause WHERE , mais pas à store_id et year_id . En revanche, la requête updateStoreDoc fait référence à ces deux champs dans la clause WHERE . Je ne sais pas si la requête ci-dessus fonctionnera correctement pour updateStatus sous cette forme sans faire référence au PK de la table.
Et une dernière chose à considérer est que le store_status La table est mise à jour dans la première requête par lots, puis référencée dans la deuxième requête par lots. Vous devrez peut-être changer l'ordre de ces requêtes en fonction du résultat final que vous attendez.
Je ne connais pas Sybase. Sur la base de mon expérience avec d'autres systèmes de base de données, la façon dont vos requêtes sont configurées, même si vous utilisiez une table temporaire, vous devrez soit boucler à l'aide d'un curseur, soit effectuer une jointure multi-colonnes sur la table temporaire. Aucun des deux n'apporte un avantage dans la grande majorité des cas.
Dans cet esprit, à moins qu'il y ait un problème de performances nécessitant une solution de contournement de table temporaire, vous pouvez simplement faire la jointure sur les tables d'origine comme ci-dessous. Je n'ai pas testé cela, vous devriez donc confirmer la logique avant de mettre en service.
<cfquery datasource="test">
update store_status
set start_date = stores.start_date
, end_date = stores.end_date
, last_update_date = getDate()
from store_status
inner join stores on store_status.store_id = stores.store_id
and store_status.year_id = stores.year_id
WHERE stores.store_id = <cfqueryparam value="#storeID#" />
and stores.year_id = <cfqueryparam value="#yearID#" />
and stores.store_type_id IN (5,6,7,8,9,10);
update store_doc
set approve_start_date = stores.start_date
, approve_end_date = stores.end_date
, status_id = (SELECT max(status_id) FROM store_doc WHERE store_id = stores.store_id AND year_id = stores.year_id)
from store_doc
inner join stores on store_doc.store_id = stores.store_id
and store_doc.year_id = stores.year_id
WHERE stores.store_id = <cfqueryparam value="#storeID#" />
and stores.year_id = <cfqueryparam value="#yearID#" />
and stores.store_type_id IN (5,6,7,8,9,10)
</cfquery>
Oui, d'après ce que vous avez décrit, vous ne devriez pas avoir besoin de tables temporaires, de boucles ou de curseurs pour cela - juste de simples JOIN.
Plus souvent qu'autrement, si vous effectuez une mise à jour et que vous souhaitez utiliser un curseur, il existe probablement une bien meilleure façon de faire la requête.
Comme @ravioli l'a demandé, pourriez-vous clarifier ce que vous attendez des requêtes (pas une description de la syntaxe, mais quel est le résultat souhaité)? Quelle est la relation entre chacune des trois tables?
Le
store_statusest particulièrement intéressant. Peut-il y avoir plusieurs statuts par magasin / an? Pourquoi la boucle écrase-t-elle les dates de début / fin pour TOUS les enregistrements avec un certain statut - au lieu de pour un magasin spécifique + statut.Comme écrit ci-dessus, votre code ne boucle pas sur la requête
findStores. Il boucle sur la requêtefindSubAgencies. Est-ce un problème de copier / coller et votre boucle passe en fait par la requêtefindStores? Ou avez-vous besoin d'inclure l'exemple de la requêtefindSubAgencies? De plus, ces requêtes nécessitent descfqueryparam.@Shawn C'est un problème de copier / coller. Il doit s'agir d'une requête
findStores.Quelles colonnes se trouvent dans
store_status? Pour le moment, la requête mettra à jourstore_statusquel que soit lestore_id, elle n'est liée que parstatus_id.Et
find_storesrenverra-t-il plus d'un résultat? Avez-vous besoin de «boucler» ces résultats?