web-dev-qa-db-ja.com

ODBC、結合、およびグループ化を使用したSQLクエリ

以下のステートメントに問題があります。クエリの主な目的は、顧客のリストをODBCからAccessデータベースにプルすることです(顧客はIBM i上のDB2に格納されます)。クエリは顧客を探すことを目的としています( OM01U1.OM01015)13か月以上配信されていない(RS2101F.DELDATE)が、まだ抑制されていない(OM01U1.OM01068)。機器と財務記録を調査するためにすべての列が必要です。 tそれらを削除します。ただし、ステートメントはいくつかのテーブルから取得されています。問題は「グループ化」にあると思われます。結果を受け取ったときに重複したアウトレット(OM01U1.OM01015)も取得しています。表示する必要があるのはアウトレットは最後の配信日(RS2101F.DELDATE)で一度だけです。私をひどく殴らないでください。このクエリは作成しませんでした。継承しただけです。かなり離れているので、おそらくもっと良い方法があると思います。必要な情報を入手するために。助けてくれた人に感謝します!

SELECT
    OM01U1.OM01041 as Loc,
    OM01U1.OM01015 AS Outlet, 
    OM01U1.OM01945 AS OLTyp,
    OM01U1.OM01052 AS Outlet_Name,
    OM01U1.OM01054A AS Street_Number, 
    OM01U1.OM01054C AS Street_Name, 
    OM01U1.OM01055A AS City, 
    OM01U1.OM01055B AS State, 
    OM01U1.OM01106 AS Zip, 
    OM01U1.OM01058A AS Area_Code, 
    OM01U1.OM01058C AS Phone, 
    OM01U1.OM01037 AS Channel, 
    OM01U1.OM01926 AS USA_Type,
    OM01U1.OM01078 AS Key_Acct, 
    OM01U1.OM01110 AS TRDGRP,
    OM01U1.OM01034 AS Trade_Name, 
    OM01U1.OM01248 AS DTC,
    OM01U1.OM01073 AS Sales_Route, 
    OM01U1.OM01068 AS Sup_CDE, 
    OM01U1.OM01982 AS Sup_Reason, 
    OM01U1.OM01067 AS Sup_Date, 
    OM01U1.OM01065 AS Creation_DT, 
    OM01U1.OM01066 AS Update_DT, 
    OM01U1.OM01141 AS CAN_NUM,
    CF30_CNSTYP AS CF_CONTYP, 
    EC01_EQUNUM AS EC_EQUIP,
    EC01_PRMSLR AS EC_SUPPLIER, 
    EC01_EQUOWN AS EC_OWN_CD,
    EC01_EQUACY AS EC_ACCESSORY,
    EC01_EQUSPP AS EC_COMPONENT,
    EC01_SYSSTA AS EC_STATUS,
    EC01_ISTDAT AS EC_INSTALL_DT, 
    EC01_RSPPRN AS EC_PERSON, 
    EC01_LASMTCDAT AS EC_LAST_MNT_DT, 
    OC02_RNTCTCNUM AS OC_CONT_NUM, 
    OC02_RNTCTCSTA AS OC_CONT_STATUS, 
    OC02_BTONUM AS OC_BILL_TO, 
    RS2101F.DELDATE AS RS_LAST_DEL_DT
  FROM 
    CF30, EC01, OC02, OM01U1, RS2101F
  WHERE 
    OM01U1.OM01015 = RS2101F.OUTNUM

  GROUP BY
    RS2101F.DELDATE,
    OC02_BTONUM,
    OC02_RNTCTCSTA,
    OC02_RNTCTCNUM,
    OC02_OUTNUM,
    EC01_LASMTCDAT,
    EC01_RSPPRN,
    EC01_ISTDAT,
    EC01_EQUOWN,
    EC01_EQUACY,
    EC01_EQUSPP,
    EC01_SYSSTA,
    EC01_PRMSLR,
    EC01_EQUNUM,
    CF30_CNSTYP,
    OM01U1.OM01141,
    OM01U1.OM01066,
    OM01U1.OM01065,
    OM01U1.OM01067,
    OM01U1.OM01982,
    OM01U1.OM01068,
    OM01U1.OM01073,
    OM01U1.OM01248,
    OM01U1.OM01034,
    OM01U1.OM01110,
    OM01U1.OM01078,
    OM01U1.OM01926,
    OM01U1.OM01037,
    OM01U1.OM01058C,
    OM01U1.OM01058A,
    OM01U1.OM01106,
    OM01U1.OM01055B,
    OM01U1.OM01055A,
    OM01U1.OM01054C,
    OM01U1.OM01054A,
    OM01U1.OM01052,
    OM01U1.OM01945,
    OM01U1.OM01015,
    OM01U1.OM01041
  HAVING  RS2101F.DELDATE < 1110809
      AND OM01U1.OM01068<>'S' 
3
KNG

動作するクエリがありますが、次を選択しています。

FROM CF30, EC01, OC02, OM01U1, RS2101F 

明示的な結合はなく、暗黙的な結合は1つだけです。

WHERE OM01U1.OM01015 = RS2101F.OUTNUM

これは問題につながるでしょう。各テーブルのどのフィールド(列)がどのフィールド(列)に一致するかを見つけることができますか?次に、次のように言うことができます。

FROM OM01U1 
INNER JOIN RS2101F
ON OM01U1.OM01015 = RS2101F.OUTNUM

例えば。

派生テーブルを追加すると便利な場合もあります。

SELECT OUTNUM, Max(RS2101F.DELDATE) FROM RS2101F

したがって、次のようになります。

FROM OM01U1 
INNER JOIN (
     SELECT OUTNUM, Max(RS2101F.DELDATE) 
     FROM RS2101F
     GROUP BY OUTNUM) As t
ON OM01U1.OM01015 = t.OUTNUM
5
Remou

ウィンドウ関数を使用して、データを分割できます。あなたのデータをもっとよく知っていれば、副選択なしで書き直したいと思いますが、テキストの壁が頭を痛めています;)

どうぞ:

SELECT * FROM
(
SELECT ROW_NUMBER() OVER(PARTITION BY OM01U1.OM01015 ORDER BY EC01_LASMTCDAT DESC) as r,
OM01U1.OM01041 as Loc, 
OM01U1.OM01015 AS Outlet, 
-- SNIP --
-- SNIP --
RS2101F.DELDATE AS RS_LAST_DEL_DT, 
EC01_LASMTCDAT AS EC_LAST_MNT_DT, 
COUNT (EC01_EQUNUM) AS EC_EQUIP, 
COUNT (EC01_EQUACY) AS EC_ACCESSORY, 
COUNT (EC01_EQUSPP) AS EC_COMPONENT
FROM OM01U1
JOIN RS2101F ON OM01U1.OM01015 = RS2101F.OUTNUM 
LEFT JOIN CF30 ON OM01U1.OM01015 = CF30_OUTNUM 
LEFT JOIN EC01 ON OM01U1.OM01015 = EC01_EQUOUTNUM 
LEFT JOIN OC02 ON OM01U1.OM01015 = OC02_OUTNUM
GROUP BY EC01_LASMTCDAT,EC01_SYSSTA,EC01_EQUNUM,EC01_EQUSPP,EC01_EQUACY,
CF30_CNSTYP,OM01U1.OM01141,OM01U1.OM01066,OM01U1.OM01065,
OM01U1.OM01067,OM01U1.OM01982,OM01U1.OM01068,OM01U1.OM01073
,OM01U1.OM01248,OM01U1.OM01034,OM01U1.OM01110,OM01U1.OM01078,OM01U1.OM01926,
OM01U1.OM01037,OM01U1.OM01058C,OM01U1.OM01058A,OM01U1.OM01106,OM01U1.OM01055B,
OM01U1.OM01055A,OM01U1.OM01054C,OM01U1.OM01054A,OM01U1.OM01052,OM01U1.OM01945,
OM01U1.OM01015,OM01U1.OM01041,RS2101F.DELDATE,OC02_RNTCTCSTA
HAVING 
RS2101F.DELDATE < 1110815 
AND EC01_LASMTCDAT < 1110815 
AND OM01U1.OM01068<>'S' 
)
WHERE r=1;

このビット:

SELECT ROW_NUMBER() OVER(PARTITION BY OM01U1.OM01015 ORDER BY EC01_LASMTCDAT DESC) as r

...顧客(OM01U1.OM01015)と最終保守日(EC01_LASMTCDAT)でデータを注文し、顧客ごとに1から始まる番号(r)を顧客ごとに割り当てます。

次に、外部クエリを使用して、r = 1行だけを選択し、必要なデータを取得します。

注:私はあなたのデータを持っていないので、完全にテストされていません。

3
Philᵀᴹ

5つのファイルを結合していますが、それらの結合の1つを制限する条件は1つだけです。

WHERE OM01U1.OM01015 = RS2101F.OUTNUM

この条件は、参加仕様に含まれている必要があります

FROM OM01U1
JOIN RS2101F     ON OM01U1.OM01015 = RS2101F.OUTNUM
JOIN CF30        ON _____ = ______
JOIN EC01        ON _____ = ______
JOIN OC02        ON _____ = ______

これらのファイルの一部に一致する行がない可能性がある場合、それらのファイルはLEFT JOINを使用する必要があり、そのテーブルの列の値は、ファイル定義に応じてデフォルト値またはnullになります。
このシステムのテーブルの列は、ほとんどの場合、NOT NULL WITHDEFAULTとして定義されています。

GROUP BYクエリがありますが、集計関数を指定していません。 DELDATEにMAX()関数が必要になります。クエリのこの部分は、「WITH」で始まる「共通テーブル式」[CTE]に分離する必要があります。

  WITH r2 as
  (SELECT OUTNUM,
          max(DELDATE) as lastdelivery
     FROM RS2101F
     WHERE DELDATE >= (thirteenMonthsAgo)
     GROUP BY OUTNUM
  )
  SELECT ...
         ...
    FROM      OMO1U1 
    LEFT JOIN r2      ON OM01U1.OM01015 = r2.OUTNUM
    LEFT JOIN CF30    ...
    ...

DELDATEはcyymmdd形式のようです。ここで、「c」は1900年代の場合は0、2000年代の場合は1です。 UDFがない限り、SQL式ではなく、プログラムで<thirteenMonthsAgo>の値を計算することをお勧めします。

2
WarrenT

soの多くの部分を含む、長く複雑なクエリに圧倒される場合は、それを小さな部分に分割し、それらを機能させてから、小さな段階で再構築することをお勧めします。最初に「心の大きさの一口」に焦点を合わせます。一口で噛むことができる以上に噛まないでください。単純なものから始めて、レイヤーに戻します。

グループ化ロジックをメインクエリから分離することをお勧めします。数十のフィールドでグループ化する必要があることはめったになく、考えてみれば、現実をうまくモデル化することはできません。フィールドの長いリストが一致していることを確認すると、コードも見落とされがちになります。

物事をより小さく、より管理しやすいチャンクに分割する1つの戦略は、機能の一部をSQLビュー、またはMS-Access用語ではクエリに配置することです。すべてがそのように機能している場合は、すべてを1つの大きなクエリにマージして戻すかどうかを決定できます。しかし、これらの再利用可能なコンポーネントを次回のためにぶら下げておくと便利な場合があります。

また、小さくて単純なクエリを使用することから始めると、たとえばコンマの欠落など、偶発的な構文エラーを見つけるのがはるかに簡単になります。

サブクエリまたは一般的なテーブル式を使用して、物事をチャンクに分割したり、すでに作成したチャンクを結合したりすることもできます。個人的には、一般的なテーブル式を便利な段階的な構成要素と考える傾向があります。次のようになります。

WITH a as
(               -- first step
  select ...
    ...
), b as
(               -- second step
  select ...
    ...
)
SELECT ...      -- third step
  FROM      a
       JOIN b            on a.key1 = b.ky1
  LEFT JOIN tablec as c  on b.ky2  = c.xyz

[コンマに関しては、行末ではなく行頭に配置して、コンマ(またはコンマがないこと)を見つけやすくすることがよくあります。あなたの目は最初は簡単に線を見下ろすことができますが、最後にはそれほど速くて単純ではありません。また、グループの最後に行を追加したり、行を移動したりするのも簡単になると思います。最初は奇妙に見えるかもしれませんが、カンマの欠落などの小さなエラーを見つけるのが非常に難しい場合があるため、役に立ちました。]

表示したグループ化ロジックを削除し、他のテーブルを削除すると、次のようになります。

SELECT  EC01_EQUOUTNUM
      , EC01_SYSSTA     AS EC_STATUS
      , EC01_LASMTCDAT  AS EC_LAST_MNT_DT
      , COUNT(EC01_EQUNUM)  AS EC_EQUIP
      , COUNT(EC01_EQUACY)  AS EC_ACCESSORY
      , COUNT(EC01_EQUSPP)  AS EC_COMPONENT
  FROM  EC01    
  GROUP BY EC01_EQUOUTNUM
     , EC01_SYSSTA
     , EC01_LASTMTCDAT

これにはまだいくつかの問題があります。質問で、最終結果セットにコンセントごとに1行を含めるようにしたいとおっしゃいました。ただし、これにより、GROUPBY句のすべてのフィールドの値の組み合わせごとに1つの要約レコードが得られます。それが本当にあなたが望むものだとは思えません。機器ごとに1行が必要だったと考えられますが、機器でCOUNT()を使用しているようです。 Outletごとに1行が必要だと言ったので、それがGROUPBY句の唯一の列である必要があります。 GROUP BY句にない他のすべての列は、集計関数を使用する必要があります。 MAX()を使用するのが理にかなっているものもあれば、意味がないものもあります。これらの場合、これらの列を別のソースから取得するか、アウトレットレベルに含めるのがまったく意味がないかを検討する必要があります。更新されたクエリですでにこれに取り組んでいるようですが、StatusとLast_Maint_Dateは残っています。 Last_Maint_DateはMAX()にとって自然なようです。特定のステータスを除外したいという理由だけでステータスはありますか?その場合は、それをWHERE句に移動します。それ以外の場合は、MAX()またはMIN()を使用できます。

1
WarrenT

@WarrenTわかりました、それは今では理にかなっています。ステートメントを正しい場所に配置しているかどうかはわかりませんが、「WITH」ステートメントでエラーが発生し続けます。重複の原因となっているEC01レコード(機器)に絞り込みました。コンセントに接続されている各機器の最後のメンテナンス記録を取得しています。アウトレットのすべてのメンテナンス日(EC01_LASMTCDAT)のうち、最新のメンテナンス日が必要です。アウトレットの最後のEC01_LASMTCDAT(最後のメンテナンス日)のみをプルするには、どのステートメントを使用する必要がありますか?これが私の更新されたクエリです...

SELECT
OM01U1.OM01041 as Loc,
OM01U1.OM01015 AS Outlet, 
OM01U1.OM01945 AS OLTyp,
OM01U1.OM01052 AS Outlet_Name,
OM01U1.OM01054A AS Street_Number, 
OM01U1.OM01054C AS Street_Name, 
OM01U1.OM01055A AS City, 
OM01U1.OM01055B AS State, 
OM01U1.OM01106 AS Zip, 
OM01U1.OM01058A AS Area_Code, 
OM01U1.OM01058C AS Phone, 
OM01U1.OM01037 AS Channel, 
OM01U1.OM01926 AS USA_Type,
OM01U1.OM01078 AS Key_Acct, 
OM01U1.OM01110 AS TRDGRP,
OM01U1.OM01034 AS Trade_Name, 
OM01U1.OM01248 AS DTC,
OM01U1.OM01073 AS Sales_Route, 
OM01U1.OM01068 AS Sup_CDE, 
OM01U1.OM01982 AS Sup_Reason, 
OM01U1.OM01067 AS Sup_Date, 
OM01U1.OM01065 AS Creation_DT, 
OM01U1.OM01066 AS Update_DT, 
OM01U1.OM01141 AS CAN_NUM,
OC02_RNTCTCSTA AS OC_CONT_STATUS, 
CF30_CNSTYP AS CF_CONTYP,
EC01_SYSSTA AS EC_STATUS, 
RS2101F.DELDATE AS RS_LAST_DEL_DT,
EC01_LASMTCDAT AS EC_LAST_MNT_DT,
COUNT
(EC01_EQUNUM) AS EC_EQUIP,
COUNT
(EC01_EQUACY) AS EC_ACCESSORY,
COUNT
(EC01_EQUSPP) AS EC_COMPONENT

FROM 
OM01U1

JOIN RS2101F ON OM01U1.OM01015 = RS2101F.OUTNUM
LEFT JOIN  CF30 ON OM01U1.OM01015 = CF30_OUTNUM
LEFT JOIN  EC01 ON OM01U1.OM01015 = EC01_EQUOUTNUM
LEFT JOIN OC02 ON OM01U1.OM01015 = OC02_OUTNUM

GROUP BY
EC01_LASMTCDAT,EC01_SYSSTA,EC01_EQUNUM,EC01_EQUSPP,EC01_EQUACY,CF30_CNSTYP,OM01U1.OM01141,OM01U1.OM01066,OM01U1.OM01065,OM01U1.OM01067,OM01U1.OM01982,OM01U1.OM01068,OM01U1.OM01073,OM01U1.OM01248,OM01U1.OM01034,OM01U1.OM01110,OM01U1.OM01078,OM01U1.OM01926,OM01U1.OM01037,OM01U1.OM01058C,OM01U1.OM01058A,OM01U1.OM01106,OM01U1.OM01055B,OM01U1.OM01055A,OM01U1.OM01054C,OM01U1.OM01054A,OM01U1.OM01052,OM01U1.OM01945,OM01U1.OM01015,OM01U1.OM01041,RS2101F.DELDATE,OC02_RNTCTCSTA

HAVING
RS2101F.DELDATE < 1110815
AND EC01_LASMTCDAT < 1110815
AND OM01U1.OM01068<>'S' 
AND OM01U1.OM01015<>9999999
AND OM01U1.OM01945 Not In ('CRC','CRD','PER','PMO')
AND OM01U1.OM01110<>'CCNA'
AND OM01U1.OM01248<>'114'
AND OM01U1.OM01073 <> '398'
0
KNG