This one is worse than the last one. It's hard to believe this is a single SQL statement.
SELECT from_lane.lane_id, from_lane.lane_type from_lane_type_id,
from_lane.city from_lane_city, from_lane.state_cd from_lane_state_cd,
from_lane.country_cd from_lane_country_cd,
from_lane.low_zip from_lane_low_zip,
from_lane.high_zip from_lane_high_zip, from_lane.other from_lane_other,
to_lane.lane_type to_lane_type_id, to_lane.city to_lane_city,
to_lane.state_cd to_lane_state_cd, to_lane.country_cd to_lane_country_cd,
to_lane.low_zip to_lane_low_zip,
to_lane.high_zip to_lane_high_zip, to_lane.other to_lane_other
FROM (SELECT ID lane_id, from_key_num lane_type, NULL city,
from_low_key_value state_cd, null country_cd, NULL low_zip, NULL high_zip,
NULL other
FROM tms_lane_pre
WHERE from_key_num = 2
UNION
SELECT ID lane_id, from_key_num lane_type, NULL city,
CASE
WHEN c1.min_state = c1.max_state
AND c1.min_state = c2.min_state
AND c1.min_state = c2.max_state
THEN c1.min_state
ELSE NULL
END AS state_cd,
CASE
WHEN c1.min_country = c1.max_country
AND c1.min_country = c2.min_country
AND c1.min_country = c2.max_country
THEN c1.min_country
ELSE NULL
END AS country_cd,
from_low_key_value || '00' low_zip,
from_high_key_value || '99' high_zip, NULL other
FROM tms_lane_pre p
LEFT OUTER JOIN
(SELECT SUBSTR (zip, 1, 3) zip3, MIN (state) min_state,
MAX (state) max_state, MIN(country) min_country,
max(country) max_country
FROM tms_centroid_dtl_pre
GROUP BY SUBSTR (zip, 1, 3)) c1 ON p.from_low_key_value =
c1.zip3
LEFT OUTER JOIN
(SELECT SUBSTR (zip, 1, 3) zip3, MIN (state) min_state,
MAX (state) max_state,MIN(country) min_country,
max(country) max_country
FROM tms_centroid_dtl_pre
GROUP BY SUBSTR (zip, 1, 3)) c2
ON p.from_high_key_value = c2.zip3
WHERE from_key_num = 3
UNION
SELECT ID lane_id, from_key_num lane_type, from_low_key_value city,
SUBSTR (from_high_key_value, 1, 4) state_cd, country,
MIN (zip) low_zip, MAX (zip) high_zip, NULL other
FROM tms_lane_pre p LEFT OUTER JOIN tms_centroid_dtl_pre c
ON p.from_low_key_value = c.city
AND p.from_high_key_value = c.state
WHERE p.from_key_num = 4
GROUP BY ID, from_key_num, from_low_key_value, from_high_key_value, country
UNION
SELECT ID lane_id, from_key_num lane_type,
CASE
WHEN c1.min_city = c1.max_city
AND c1.min_city = c2.min_city
AND c1.min_city = c2.max_city
THEN c1.min_city
ELSE NULL
END city,
CASE
WHEN c1.min_state = c1.max_state
AND c1.min_state = c2.min_state
AND c1.min_state = c2.max_state
THEN c1.min_state
ELSE NULL
END state_cd,
CASE
WHEN c1.min_country = c1.max_country
AND c1.min_country = c2.min_country
AND c1.min_country = c2.max_country
THEN c1.min_country
ELSE NULL
END country_cd,
from_low_key_value low_zip, from_high_key_value high_zip,
NULL other
FROM tms_lane_pre p
LEFT OUTER JOIN
(SELECT zip, MIN (state) min_state, MAX (state) max_state,
MIN (country) min_country, MAX (country) max_country,
MIN (city) min_city, MAX (city) max_city
FROM tms_centroid_dtl_pre
GROUP BY zip) c1 ON p.from_low_key_value = c1.zip
LEFT OUTER JOIN
(SELECT zip, MIN (state) min_state, MAX (state) max_state,
MIN (country) min_country, MAX (country) max_country,
MIN (city) min_city, MAX (city) max_city
FROM tms_centroid_dtl_pre
GROUP BY zip) c2 ON p.from_high_key_value = c2.zip
WHERE from_key_num = 5
UNION
SELECT ID lane_id, from_key_num lane_type, NULL city, NULL state, null country,
from_low_key_value low_zip, from_high_key_value high_zip,
NULL other
FROM tms_lane_pre
WHERE from_key_num = 6
UNION
SELECT ID lane_id, from_key_num lane_type, NULL city, NULL state, null country,
NULL low_zip, NULL high_zip, from_low_key_value other
FROM tms_lane_pre
WHERE from_key_num = 7) from_lane,
(SELECT ID lane_id, to_key_num lane_type, NULL city,
to_low_key_value state_cd, null country_cd, NULL low_zip, NULL high_zip,
NULL other
FROM tms_lane_pre
WHERE to_key_num = 2
UNION
SELECT ID lane_id, to_key_num lane_type, NULL city,
CASE
WHEN c1.min_state = c1.max_state
AND c1.min_state = c2.min_state
AND c1.min_state = c2.max_state
THEN c1.min_state
ELSE NULL
END AS state_cd,
CASE
WHEN c1.min_country = c1.max_country
AND c1.min_country = c2.min_country
AND c1.min_country = c2.max_country
THEN c1.min_country
ELSE NULL
END AS country_cd,
to_low_key_value || '00' low_zip,
to_high_key_value || '99' high_zip, NULL other
FROM tms_lane_pre p
LEFT OUTER JOIN
(SELECT SUBSTR (zip, 1, 3) zip3, MIN (state) min_state,
MAX (state) max_state, MIN (country) min_country,
MAX (country) max_country
FROM tms_centroid_dtl_pre
GROUP BY SUBSTR (zip, 1, 3)) c1 ON p.to_low_key_value =
c1.zip3
LEFT OUTER JOIN
(SELECT SUBSTR (zip, 1, 3) zip3, MIN (state) min_state,
MAX (state) max_state,MIN (country) min_country,
MAX (country) max_country
FROM tms_centroid_dtl_pre
GROUP BY SUBSTR (zip, 1, 3)) c2 ON p.to_high_key_value =
c2.zip3
WHERE to_key_num = 3
UNION
SELECT ID lane_id, to_key_num lane_type, to_low_key_value city,
SUBSTR (to_high_key_value, 1, 4) state_cd,country country_cd, MIN (zip) low_zip,
MAX (zip) high_zip, NULL other
FROM tms_lane_pre p LEFT OUTER JOIN tms_centroid_dtl_pre c
ON p.to_low_key_value = c.city
AND p.to_high_key_value = c.state
WHERE p.to_key_num = 4
GROUP BY ID, to_key_num, to_low_key_value, to_high_key_value, country
UNION
SELECT ID lane_id, to_key_num lane_type,
CASE
WHEN c1.min_city = c1.max_city
AND c1.min_city = c2.min_city
AND c1.min_city = c2.max_city
THEN c1.min_city
ELSE NULL
END city,
CASE
WHEN c1.min_state = c1.max_state
AND c1.min_state = c2.min_state
AND c1.min_state = c2.max_state
THEN c1.min_state
ELSE NULL
END state_cd,
CASE
WHEN c1.min_country = c1.max_country
AND c1.min_country = c2.min_country
AND c1.min_country = c2.max_country
THEN c1.min_country
ELSE NULL
END country_cd,
to_low_key_value low_zip, to_high_key_value high_zip,
NULL other
FROM tms_lane_pre p
LEFT OUTER JOIN
(SELECT zip, MIN (state) min_state, MAX (state) max_state,
MIN (country) min_country, MAX (country) max_country,
MIN (city) min_city, MAX (city) max_city
FROM tms_centroid_dtl_pre
GROUP BY zip) c1 ON p.to_low_key_value = c1.zip
LEFT OUTER JOIN
(SELECT zip, MIN (state) min_state, MAX (state) max_state,
MIN (country) min_country, MAX (country) max_country,
MIN (city) min_city, MAX (city) max_city
FROM tms_centroid_dtl_pre
GROUP BY zip) c2 ON p.to_high_key_value = c2.zip
WHERE to_key_num = 5
UNION
SELECT ID lane_id, to_key_num lane_type, NULL city, NULL state, null country,
to_low_key_value low_zip, to_high_key_value high_zip,
NULL other
FROM tms_lane_pre
WHERE to_key_num = 6
UNION
SELECT ID lane_id, to_key_num lane_type, NULL city, NULL state, null country,
NULL low_zip, NULL high_zip, to_low_key_value other
FROM tms_lane_pre
WHERE to_key_num = 7) to_lane
WHERE from_lane.lane_id = to_lane.lane_id
Thursday, October 15, 2009
Tuesday, October 13, 2009
SQL Disaster
In keeping with the theme of my blog, I present to you one example of a horrendous SQL query my co-worker sent me. I don't know what kind of super villain created this, but I'd hate to meet him or her in a dark alley. We're hoping it's some sort of program-generated query, like what Microstrategy can produce.
This one has it all. - advanced analytical functions, aggregates, a hint override, order bys, group bys, aliases, nested sub-selects, outer joins, in clauses, between clauses, even a UNION!
Think you've seen worse? Send it to me!
SELECT /*+ ORDERED */
inv.inv_bol, inv.cost_qualifier, NVL(inv.cost_code,' '), inv.invoice_id
,inv.inv_nbr, rcv.dttm, inv.status_code, inv.inv_date, vouch.dttm AS VOUCHER_DTTM
,inv.inv_distance, inv.inv_weight, inv.inv_cube, vouch.ap_pay_date, inv.amount
FROM (SELECT i.inv_bol, c.cost_qualifier, c.cost_code, i.invoice_id
,MAX(i.inv_date) AS INV_DATE
,MAX(i.inv_nbr) AS INV_NBR
,MAX(i.status_code) AS STATUS_CODE
,MAX(i.inv_distance) AS INV_DISTANCE
,MAX(i.inv_weight) AS INV_WEIGHT
,MAX(i.inv_cube) AS INV_CUBE
,SUM(amount) AS AMOUNT
FROM (SELECT inv_bol
,LAST_VALUE(invoice_id) OVER(PARTITION BY inv_bol ORDER BY status_code DESC) INVOICE_ID
,LAST_VALUE(inv_date) OVER(PARTITION BY inv_bol ORDER BY status_code DESC) INV_DATE
,LAST_VALUE(inv_nbr) OVER(PARTITION BY inv_bol ORDER BY status_code DESC) INV_NBR
,LAST_VALUE(status_code) OVER(PARTITION BY inv_bol ORDER BY status_code DESC) STATUS_CODE
,LAST_VALUE(inv_distance) OVER(PARTITION BY inv_bol ORDER BY status_code DESC) INV_DISTANCE
,LAST_VALUE(inv_weight) OVER(PARTITION BY inv_bol ORDER BY status_code DESC) INV_WEIGHT
,LAST_VALUE(inv_cube) OVER(PARTITION BY inv_bol ORDER BY status_code DESC) INV_CUBE
,ROW_NUMBER() OVER(PARTITION BY inv_bol ORDER BY status_code DESC) SEQ
FROM
tm_fp_invoice
,(SELECT DISTINCT PRIMARY_KEY1 BOL
FROM tm_FP_AUDIT_TRAIL
WHERE TABLE_NAME = 'FP_MASTER'
AND DTTM between (select extract_from_tstmp from PM_BI_TMS_DW_CTL) and (select extract_to_tstmp from PM_BI_TMS_DW_CTL)
UNION
SELECT DISTINCT I.INV_BOL BOL
FROM tm_FP_AUDIT_TRAIL A, tm_FP_INVOICE I
WHERE TABLE_NAME = 'FP_INVOICE'
AND A.DTTM between (select extract_from_tstmp from PM_BI_TMS_DW_CTL) and (select extract_to_tstmp from PM_BI_TMS_DW_CTL)
AND A.PRIMARY_KEY1 = I.INVOICE_ID) B
WHERE tm_fp_invoice.inv_bol = b.bol) I
,tm_fp_invoice_cost C
WHERE i.seq = 1
AND i.invoice_id = c.invoice_id
AND c.cost_qualifier <= 6
GROUP BY i.inv_bol,c.cost_qualifier,c.cost_code,i.invoice_id) INV
,(SELECT
i.inv_bol
,a.dttm
,p.ap_pay_date
FROM
tm_fp_audit_trail A
,tm_fp_invoice I
,tm_fp_payment P
WHERE a.table_name = 'FP_INVOICE'
AND a.value_after = '70'
AND a.value_before != '70'
AND a.primary_key1 = TO_CHAR(i.invoice_id)
AND i.status_code IN (70,80)
AND i.invoice_id = p.invoice_id (+)) VOUCH
,(SELECT primary_key1, MAX(dttm) DTTM
FROM tm_fp_audit_trail
WHERE action_code = 'A'
AND table_name = 'FP_INVOICE'
GROUP BY primary_key1) RCV
WHERE inv.inv_bol = vouch.inv_bol (+)
AND inv.invoice_id = rcv.primary_key1 (+)
This one has it all. - advanced analytical functions, aggregates, a hint override, order bys, group bys, aliases, nested sub-selects, outer joins, in clauses, between clauses, even a UNION!
Think you've seen worse? Send it to me!
SELECT /*+ ORDERED */
inv.inv_bol, inv.cost_qualifier, NVL(inv.cost_code,' '), inv.invoice_id
,inv.inv_nbr, rcv.dttm, inv.status_code, inv.inv_date, vouch.dttm AS VOUCHER_DTTM
,inv.inv_distance, inv.inv_weight, inv.inv_cube, vouch.ap_pay_date, inv.amount
FROM (SELECT i.inv_bol, c.cost_qualifier, c.cost_code, i.invoice_id
,MAX(i.inv_date) AS INV_DATE
,MAX(i.inv_nbr) AS INV_NBR
,MAX(i.status_code) AS STATUS_CODE
,MAX(i.inv_distance) AS INV_DISTANCE
,MAX(i.inv_weight) AS INV_WEIGHT
,MAX(i.inv_cube) AS INV_CUBE
,SUM(amount) AS AMOUNT
FROM (SELECT inv_bol
,LAST_VALUE(invoice_id) OVER(PARTITION BY inv_bol ORDER BY status_code DESC) INVOICE_ID
,LAST_VALUE(inv_date) OVER(PARTITION BY inv_bol ORDER BY status_code DESC) INV_DATE
,LAST_VALUE(inv_nbr) OVER(PARTITION BY inv_bol ORDER BY status_code DESC) INV_NBR
,LAST_VALUE(status_code) OVER(PARTITION BY inv_bol ORDER BY status_code DESC) STATUS_CODE
,LAST_VALUE(inv_distance) OVER(PARTITION BY inv_bol ORDER BY status_code DESC) INV_DISTANCE
,LAST_VALUE(inv_weight) OVER(PARTITION BY inv_bol ORDER BY status_code DESC) INV_WEIGHT
,LAST_VALUE(inv_cube) OVER(PARTITION BY inv_bol ORDER BY status_code DESC) INV_CUBE
,ROW_NUMBER() OVER(PARTITION BY inv_bol ORDER BY status_code DESC) SEQ
FROM
tm_fp_invoice
,(SELECT DISTINCT PRIMARY_KEY1 BOL
FROM tm_FP_AUDIT_TRAIL
WHERE TABLE_NAME = 'FP_MASTER'
AND DTTM between (select extract_from_tstmp from PM_BI_TMS_DW_CTL) and (select extract_to_tstmp from PM_BI_TMS_DW_CTL)
UNION
SELECT DISTINCT I.INV_BOL BOL
FROM tm_FP_AUDIT_TRAIL A, tm_FP_INVOICE I
WHERE TABLE_NAME = 'FP_INVOICE'
AND A.DTTM between (select extract_from_tstmp from PM_BI_TMS_DW_CTL) and (select extract_to_tstmp from PM_BI_TMS_DW_CTL)
AND A.PRIMARY_KEY1 = I.INVOICE_ID) B
WHERE tm_fp_invoice.inv_bol = b.bol) I
,tm_fp_invoice_cost C
WHERE i.seq = 1
AND i.invoice_id = c.invoice_id
AND c.cost_qualifier <= 6
GROUP BY i.inv_bol,c.cost_qualifier,c.cost_code,i.invoice_id) INV
,(SELECT
i.inv_bol
,a.dttm
,p.ap_pay_date
FROM
tm_fp_audit_trail A
,tm_fp_invoice I
,tm_fp_payment P
WHERE a.table_name = 'FP_INVOICE'
AND a.value_after = '70'
AND a.value_before != '70'
AND a.primary_key1 = TO_CHAR(i.invoice_id)
AND i.status_code IN (70,80)
AND i.invoice_id = p.invoice_id (+)) VOUCH
,(SELECT primary_key1, MAX(dttm) DTTM
FROM tm_fp_audit_trail
WHERE action_code = 'A'
AND table_name = 'FP_INVOICE'
GROUP BY primary_key1) RCV
WHERE inv.inv_bol = vouch.inv_bol (+)
AND inv.invoice_id = rcv.primary_key1 (+)
Monday, July 20, 2009
Recovering a workflow
I've discovered a powerful option within Informatica's Workflow Manager. This might be old hat to some of you, but it was new to me, so I thought I'd share it.
In general, restarting a down workflow is kind of a pain, especially if you have one as complex as the main one I maintain:

In this workflow, I've identified a couple of "merge points". These are sessions that run all by themselves. The beauty of these specific sessions is that if the workflow fails at some point before these merge points, I can "Restart workflow from task" and once it gets up to the "merge point", I can then "restart workflow from task" at the merge point, and then let it run from there.
There's a better way - suspense.
In the Workflow General properties (Workflow -> edit -> general tab) you'll see a checkbox called "Suspend on Error". Honestly, why this isn't checked by default is kind of a mystery, because it's a beautiful, powerful thing.
When a session fails, the workflow will go into "Suspend" mode. When in suspense mode, you have the option to "recover" the workflow. Once chosen, the workflow will restart whatever down job there is, and then finish the workflow from there. The beauty of this option is that you don't have to intervene at the merge point. So, if a job that would normally run in parallel to other sessions fails, you don't have to then restart the workflow from the merge point forward. Informatica retains session status and can continue on the workflow as needed.

When recovered, the failed job gets this weird red/green hybrid line to indicate that it was failed, but then succeeded.
In general, restarting a down workflow is kind of a pain, especially if you have one as complex as the main one I maintain:
In this workflow, I've identified a couple of "merge points". These are sessions that run all by themselves. The beauty of these specific sessions is that if the workflow fails at some point before these merge points, I can "Restart workflow from task" and once it gets up to the "merge point", I can then "restart workflow from task" at the merge point, and then let it run from there.
There's a better way - suspense.
In the Workflow General properties (Workflow -> edit -> general tab) you'll see a checkbox called "Suspend on Error". Honestly, why this isn't checked by default is kind of a mystery, because it's a beautiful, powerful thing.
When a session fails, the workflow will go into "Suspend" mode. When in suspense mode, you have the option to "recover" the workflow. Once chosen, the workflow will restart whatever down job there is, and then finish the workflow from there. The beauty of this option is that you don't have to intervene at the merge point. So, if a job that would normally run in parallel to other sessions fails, you don't have to then restart the workflow from the merge point forward. Informatica retains session status and can continue on the workflow as needed.
When recovered, the failed job gets this weird red/green hybrid line to indicate that it was failed, but then succeeded.
Wednesday, July 15, 2009
I wonder if they use Informatica?
Saw this article on CNN today:
http://www.cnn.com/2009/US/07/15/quadrillion.dollar.glitch/index.html?iref=mpstoryview
Haha! It seems likely to me that someone probably had the wrong arrow pointed to a float field. Was probably some sort of insanely large transaction key field - just a huge integer. Notice there was no decimal value. So, they probably ended up sticking some gigantic numeric field into a credit float field.
Not that first time I've seen this kind of thing. Seems like it happens every couple of months, actually.
Edit checks are a good thing. I'm thinking if you code a system that handles credit card data, and a single credit comes across that's larger than the national debt, you might want to throw an error and abend.
You know - just a suggestion.
http://www.cnn.com/2009/US/07/15/quadrillion.dollar.glitch/index.html?iref=mpstoryview
Haha! It seems likely to me that someone probably had the wrong arrow pointed to a float field. Was probably some sort of insanely large transaction key field - just a huge integer. Notice there was no decimal value. So, they probably ended up sticking some gigantic numeric field into a credit float field.
Not that first time I've seen this kind of thing. Seems like it happens every couple of months, actually.
Edit checks are a good thing. I'm thinking if you code a system that handles credit card data, and a single credit comes across that's larger than the national debt, you might want to throw an error and abend.
You know - just a suggestion.
Tuesday, June 16, 2009
Backup your repository
When was the last time you backed up your production repository server? Too long? That's what I figured.
It's easy.
Log onto your production server, and sign in with pmrep
pmrep will want your domain name and repository service name. This is why it's helpful to have the admin console loaded so you can reference those names easily. If you're like me, you don't play admin very often, so I tend to forget what the actual names are.
pmrep is kind of an odd command, in that it's an interactive command line program. So, when you run pmrep, you'll have to then run a "connect" command. Once you're connected, then you can run a "backup" command. Type help for a list of possible commands.
For backup, just use -o and give it a filename. Easy.
Another useful command here is "truncatelog" - pass it something like a -t 90 option to remove all logs older than 90 days.
It's easy.
Log onto your production server, and sign in with pmrep
pmrep will want your domain name and repository service name. This is why it's helpful to have the admin console loaded so you can reference those names easily. If you're like me, you don't play admin very often, so I tend to forget what the actual names are.
pmrep is kind of an odd command, in that it's an interactive command line program. So, when you run pmrep, you'll have to then run a "connect" command. Once you're connected, then you can run a "backup" command. Type help for a list of possible commands.
For backup, just use -o and give it a filename. Easy.
Another useful command here is "truncatelog" - pass it something like a -t 90 option to remove all logs older than 90 days.
Monday, June 8, 2009
Mystery solved
Well, sort of. I don't know why I couldn't see the "advanced transformations". I re-installed my client, and I have them now.
Apparently, Union and a bunch of other transformations are considered "advanced" and show up in a separate toolbar. I didn't even have an option to show the advanced toolbar. The re-install seems to have fixed it.
I know you were all so worried about it. :)
In case anyone cares, this is what Informatica considers an "Advanced Transformation":
Data Masking
Flexible Target Key
HTTP
Web Services
SQL
Union
Java
XML Parser
XML Generator
Unstructured Data
Salesforce Lookup, Picklist & Merge
Apparently, Union and a bunch of other transformations are considered "advanced" and show up in a separate toolbar. I didn't even have an option to show the advanced toolbar. The re-install seems to have fixed it.
I know you were all so worried about it. :)
In case anyone cares, this is what Informatica considers an "Advanced Transformation":
Data Masking
Flexible Target Key
HTTP
Web Services
SQL
Union
Java
XML Parser
XML Generator
Unstructured Data
Salesforce Lookup, Picklist & Merge
Thursday, June 4, 2009
Union Transformations?
In a comment to my previous entry, Divya asked me to check out an Informatica quiz here: http://tinyurl.com/infaquiz
Tough quiz. Some of the questions were pretty tough. One in particular threw me for a loop - one about Union transformations.
While trying to answer it, it occurred to me that I had no idea what a Union transformation was. After the quiz, I pulled up Informatica's help on this topic, and found Union Transformation in the help, but couldn't actually add one to any mapping.
I found this:
To create a Union transformation:
1. In the Mapping Designer, click Transformations > Create.
2. Select Union Transformation and enter the name of the transformation.
...
Except that when I try step 2, I fail - because Union Transformation isn't a choice for me in my drop-down. Anyone have any idea what is going on here? Is this a transformation that doesn't come with all default installations of Informatica?
Tough quiz. Some of the questions were pretty tough. One in particular threw me for a loop - one about Union transformations.
While trying to answer it, it occurred to me that I had no idea what a Union transformation was. After the quiz, I pulled up Informatica's help on this topic, and found Union Transformation in the help, but couldn't actually add one to any mapping.
I found this:
To create a Union transformation:
1. In the Mapping Designer, click Transformations > Create.
2. Select Union Transformation and enter the name of the transformation.
...
Except that when I try step 2, I fail - because Union Transformation isn't a choice for me in my drop-down. Anyone have any idea what is going on here? Is this a transformation that doesn't come with all default installations of Informatica?
Subscribe to:
Posts (Atom)