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_status
est 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_status
quel que soit lestore_id
, elle n'est liée que parstatus_id
.Et
find_stores
renverra-t-il plus d'un résultat? Avez-vous besoin de «boucler» ces résultats?