postgres: dynamic subquery in join filtering - sql

Currently trying to define a dynamic query in Postgres. Essentially the filtering done by a subquery used in the Join depends on the results for each row. The idea is that each “name” in the query will only return one project_id based on a specific set of parameters.
The basic query without any filtering:
SELECT a.name, rproj.raproject_id, rproj.current_status, rproj.create_date
FROM focalpoint fp
JOIN agl_asset a ON a.serial_number::text = fp."RoutineNumber"::text
JOIN agl_raproject AS rproj ON a.asset_id = rproj.asset_id
JOIN agl_auditproject AS audit ON rproj.auditproject_id = audit.auditproject_id
ORDER BY a.name
That would return:
name raproject_id current_status create_date
AssetA 405323966463427000 Review 24/10/2014 18:35
AssetA 405323966463460000 Review 07/10/2016 14:04
AssetA 405323966463413000 Risk Identification 28/11/2013 14:16
AssetA 405323966463413000 Closed 21/11/2013 17:33
AssetB 405323966463412000 Monitoring 15/11/2013 11:26
AssetB 405323966463427000 Review 24/10/2014 18:35
AssetB 405323966463461000 Assessment 13/10/2016 10:32
AssetB 405323966463412000 Closed 15/11/2013 11:44
But I only want one “project” per asset. If I was just trying to get the “newest” based on the create_date it would be:
SELECT a.name, rproj.raproject_id, rproj.current_status, rproj.create_date
FROM focalpoint fp
JOIN agl_asset a ON a.serial_number::text = fp."RoutineNumber"::text
JOIN agl_raproject AS rproj ON a.asset_id = rproj.asset_id AND rproj.create_date = ((SELECT max(rproj2.create_date) AS max
FROM agl_raproject rproj2
JOIN agl_auditproject audit ON rproj2.auditproject_id = audit.auditproject_id
WHERE a.asset_id = rproj2.asset_id AND audit.project_type::text = 'ngERMAssessment'::text))
JOIN agl_auditproject AS audit ON rproj.auditproject_id = audit.auditproject_id
ORDER BY a.name
But what I need is:
If there’s any project, for this specific asset, where the
current_status is “Monitoring” – Return that row
If not, take the newest one (as I’ve done already on the last query).
But again, only one project from raproject should be returned per asset.
EDIT:
The expected return would be:
name raproject_id current_status create_date
AssetA 405323966463460000 Review 07/10/2016 14:04
AssetB 405323966463412000 Monitoring 15/11/2013 11:26

distinct on
select distinct on (a.name)
a.name, rproj.raproject_id, rproj.current_status, rproj.create_date
from
focalpoint fp
inner join
agl_asset a on a.serial_number::text = fp."RoutineNumber"::text
inner join
agl_raproject as rproj on a.asset_id = rproj.asset_id
inner join
agl_auditproject as audit on rproj.auditproject_id = audit.auditproject_id
order by a.name, rproj.current_status <> 'Monitoring', rproj.create_date desc
Check the order by requirement

Related

Selecting Rows That Have One Value but Not Another

I need a get some rows of two tables with join that shoud have one value in a column (1407) but shouldn't have other value (1403)
These is the tables and the query:
select a.job, a.date, b.group from log a inner join active_tmp b
on a.jobno=b.jobno and a.no=b.no where b.list = 'N'
AND LOGDATE = TO_CHAR(TRUNC(SYSDATE),'YYYYMMDD')
and a.job not like 'HOUSE%'
and a.job not like 'CAR%' and (errorCode=1047 and errorCode<>1403);
LOG
JOB DATE LOGDATE JOBNO NO errorCode
MAM 20220123 20220125 33 22 1047
MAM 20220123 20220125 33 22 1403
DAD 20220122 20220125 11 99 1047
MAM 20220122 20220125 33 22 0323
DAD 20220122 20220125 11 99 0444
ACTIVE_TMP
JOB JOBNO NO GROUP LIST
MAM 33 22 LAPTOP N
MAM 33 22 LAPTOP N
DAD 11 99 KEY N
But I get:
MAM,20220123,LAPTOP
DAD,20220122,KEY
I need:
DAD,20220122,KEY
Because MAM have both codes (1047 and 1043).
To rephrase, I think you mean "I want to return matching rows that have error code 1047 but for which the same values of jobno, no, list do not have a corresponding row with error code 1403"
This part is redundant:
AND (errorCode = 1047 AND errorCode <> 1403);
If you are saying errorCode must be 1047, you are also saying it is not equal to 1403.
I think you want to select some rows into some result set, then check that there's not another row that disqualifies one of the selected rows from the final result.
So,
SELECT a.job,
a.date,
b.group
FROM _log a
INNER JOIN _active_tmp b
ON a.jobno = b.jobno
AND a.no = b.no
WHERE b.list = 'N'
AND LOGDATE = TO_CHAR(CURRENT_TIMESTAMP,'YYYYMMDD')
AND a.job NOT LIKE 'HOUSE%'
AND a.job NOT LIKE 'CAR%'
AND a.errorCode = 1047
AND NOT EXISTS (SELECT 1
FROM _log c
INNER JOIN _active_tmp d
ON c.jobno = d.jobno
AND c.no = d.no
WHERE a.job = c.job
AND a.date = c.date
AND b.group = d.group
AND c.errorCode = 1403)
We select the rows that satisfy the join and have error code 1047 then subtract from that set those rows that also satisfy the join but have error code 1403. You could possibly make this more terse using CTE or a temp table, but this works too.
Note I had to change a few things to make it work in my engine (Postgres), so you may have to change a few things back to Oracle.
You need to change the error code logic. Identify what JOB values has 1403 and then exclude those values
select distinct a.job, a.date, b.[group] from LOG a inner join active_tmp b
on a.jobno=b.jobno and a.no=b.no where b.list = 'N'
AND LOGDATE = TO_CHAR(TRUNC(SYSDATE),'YYYYMMDD')
and a.job not like 'HOUSE%'
and a.job not like 'CAR%' and a.job not in (select JOB from log where errorCode in(1403));

Creating Daily In-Use table w/ Zeros When NULL

Hello Stack Community,
I am not sure if I titled this accurately, but I am attempting to create a table that tracks the daily in-use quantity by product code. Currently my code drops dates where a product isn't in-use whereas I need that to show as a 0.
My thoughts where that by using the date from the date table that my LEFT OUTER JOIN with the ISNULL on the field would produce a 0, but nay.
Here is my code, with a screenshot of what it outputs with the red square highlighting where it's missing date records that I need to show as 0 :
SELECT
DD.DATE,
DE.PRODUCT_CODE,
--OOC = OUT OF CONTEXT, EITHER ISN'T CHARGEABLE OR ISN'T CURRENTLY ACTIVE
ISNULL(SUM(LIDV.QTY - LIDV.QTYSUB),0),
OD.LOCATION,
OD.SOURCE
FROM Dim_Date AS DD
LEFT OUTER JOIN ORDERv_DatesDays AS OD ON DD.DATE BETWEEN OD.SHIP_DATE AND OD.adjRETURN_DATE
LEFT OUTER JOIN FACT_Orders_LIDs AS LIDV ON LIDV.SORDERID_DAX = OD.SORDERID_DAX
LEFT OUTER JOIN DIM_ECODES AS DE ON DE.PRODUCT_CODE = LIDV.eCODE
WHERE
--DD.DATE = '3/1/2017' AND
DD.DATE BETWEEN '1/1/2017' AND EOMONTH( DATEADD( MONTH , -1, CURRENT_TIMESTAMP ) ) AND
DE.PRODUCT_CODE = '07316-' AND
YEAR(DD.DATE) = 2017
GROUP BY
DD.DATE,
DE.PRODUCT_CODE,
OD.LOCATION,
OD.SOURCE
ORDER BY
DD.DATE
I also thought, since I'm no SQL expert, that perhaps I need to just create a table with each product code and date for a specified date range but I got tripped up trying to create that as well.
Thank you for any assistance, if I need to add more info just let me know what I'm missing.
This WHERE predicate is killing your left join:
DE.PRODUCT_CODE = '07316-' AND
If product_code 07316 was not "out on loan" (or whatever) between Feb 24 and April 6 then all those rows would have looked like:
DATE PRODUCT_CODE INUSE LOCATION
2017-02-25 NULL NULL NULL
2017-02-26 NULL NULL NULL
2017-02-27 NULL NULL NULL
2017-02-28 NULL NULL NULL
...
2017-04-05 NULL NULL NULL
But, that NULL in product_code means that when the where clause asks "is NULL equal to 07316- ?" the answer is false, so the row diasppears from the resultset
Consider
LEFT OUTER JOIN DIM_ECODES AS DE
ON
DE.PRODUCT_CODE = LIDV.eCODE AND
DE.PRODUCT_CODE = '07316-'
You might also want to make some changes in the SELECT block too:
'07316-' as PRODUCT_CODE,
COALESCE(INUSE,0) AS INUSE
It might make more sense to you to write it like this:
FROM
Dim_Date AS DD
LEFT OUTER JOIN
(
SELECT
OD.SHIP_DATE,
OD.adjRETURN_DATE,
LIDV.QTY,
LIDV.QTYSUB,
OD.LOCATION,
OD.SOURCE
FROM
ORDERv_DatesDays AS OD
INNER JOIN FACT_Orders_LIDs AS LIDV ON LIDV.SORDERID_DAX = OD.SORDERID_DAX
INNER JOIN DIM_ECODES AS DE ON DE.PRODUCT_CODE = LIDV.eCODE
WHERE
DE.PRODUCT_CODE = '07316-'
) x
ON DD.DATE BETWEEN x.SHIP_DATE AND x.adjRETURN_DATE
WHERE
This is "list of dates on the left" and "any relevant data, already joined together and where'd on the right"
It should also be noted that if you're doing this for multiple product codes, to prevent just a single date row if both product 07316 and 07317 are in use on the 28th Feb you'd need to:
FROM
(
SELECT DISTINCT DD.DATE, DE.PRODUCT_CODE
FROM Dim_Date AS DD CROSS JOIN DIM_ECODES DE
WHERE ..date range clause..
)
This takes your list of dates, and crosses it with your list of prod codes, so you can be certain there are at least these two rows:
2017-02-28 07316-
2017-02-28 07317-
Then when you left join the products on date and product code, both those rows' data survive the left join, and become associated with nulls:
2017-02-28 07316- NULL NULL
2017-02-28 07317- NULL NULL
Without doing that CROSS, you'd have just one row (null in product code)

Select only the most imminent dated record from multiple records

Using MS_SQL
I have a query where I am trying to return only the NEXT event (based on today's date) from a list of future events - whereby there may be multiple future occurrences for the record (entity_name) in the database.
Here is my code so far:
select fut.event_id, ent.entity_name, evt.event_datetime_utc
from evt_v1.ce_events_coverage_future fut
left join evt_v1.ce_events evt
on evt.event_id=fut.event_id
left join edm_v1.edm_entity ent
on ent.factset_entity_id=fut.factset_entity_id
left join edm_v1.edm_security_entity_map map
on map.factset_entity_id=ent.factset_entity_id
left join evt_v1.ce_event_types typ
on typ.event_type=evt.event_type
left join evt_v1.ce_market_times mkt
on mkt.market_time=evt.market_time
left join evt_v1.ce_fiscal_periods fis
on fis.fiscal_period=evt.fiscal_period
where map.isin in
('US38259P5089',
'US0378331005')
and evt.event_type='ER'
and evt.event_datetime >=GetDate()
order by ent.entity_name asc, event_datetime asc
Which returns following results:
event_id entity_name event_datetime_utc
4097237 APPLE INC 2014-04-22 00:00:00.000
4188165 APPLE INC 2014-07-22 00:00:00.000
4270116 APPLE INC 2014-10-20 00:00:00.000
4339538 APPLE INC 2015-01-20 00:00:00.000
4097141 GOOGLE INC 2014-04-16 00:00:00.000
4188066 GOOGLE INC 2014-07-17 00:00:00.000
4269906 GOOGLE INC 2014-10-16 00:00:00.000
4339013 GOOGLE INC 2015-01-22 00:00:00.000
All I want to return is the most imminent records for each entity_name:
4097237 APPLE INC 2014-04-22 00:00:00.000
4097141 GOOGLE INC 2014-04-16 00:00:00.000
I'm sure this is quite straightforward but I'm not sure if this is best approached by using DATE functions, or some sort of RANK function (or neither!)?
Thanks in advance for your help,
E.
You can use rank() or row_number():
with t as (<your query here>)
select event_id, entity_name, event_datetime_utc
from (select t.*,
row_number() over (partition by entity_name
order by event_datetime_utc asc
) as seqnum
from t
) t
where seqnum = 1;
Firstly, you could use a subquery to find those values. This depends on the assumption that evt_v1.ce_events.event_datetime is not null. In addition, if the additional criteria on map.isin is required for determining "next", you can simply expand the subquery.
Select fut.event_id, ent.entity_name, evt.event_datetime_utc
From evt_v1.ce_events_coverage_future As fut
Join evt_v1.ce_events As evt
On evt.event_id = fut.event_id
Join edm_v1.edm_entity As ent
On ent.factset_entity_id = fut.factset_entity_id
Join edm_v1.edm_security_entity_map As map
On map.factset_entity_id = ent.factset_entity_id
Left Join evt_v1.ce_event_types As typ
On typ.event_type = evt.event_type
Left Join evt_v1.ce_market_times As mkt
On mkt.market_time = evt.market_time
Left Join evt_v1.ce_fiscal_periods As fis
On fis.fiscal_period = evt.fiscal_period
Where map.isin In( 'US38259P5089','US0378331005' )
And evt.event_type = 'ER'
And evt.event_datetime = (
Select Min( evt1.event_datetime )
From evt_v1.ce_events_coverage_future As fut1
Join evt_v1.ce_events As evt1
On evt1.event_id = fut1.event_id
Join edm_v1.edm_entity As ent1
On ent1.factset_entity_id = fut1.factset_entity_id
Where ent1.entity_name = ent.entity_name
And evt1.event_datetime >= CURRENT_TIMESTAMP
)
Order By ent.entity_name Asc, evt.event_datetime Asc
Secondly, you have used Left Joins in a number of places where it will not make any difference. Specifically, you have a Left Join on edm_v1.edm_security_entity_map but in the Where clause you are requiring that map.isin have a value by virtue of your In function and thus have effectively converted this Left Join into an inner Join. The same is true of the edm_v1.edm_security_entity_map table.

Sum Values within 3 tables

Table 1
jh."job-hdr"
job-date job-disp job-dept job-route job-id job-no
01/04/2013 6467 abc 123 22 81088
01/04/2013 6468 abc 987 36 82568
Table 2
rh."rec-charge"
charge-type rec-id base-sales-value
XYZ 22 700
Table 3
rc."rec-cost"
charge-type rec-id base-cost-value
XYZ 22 300
I need to be able to get the profit from this jobid of
700 - 300 = 400
This is where I have gotten up to
SELECT jh."job-date", jh."job-disp", jh."job-dept", jh."job-route", rc."charge-type",rh."charge-type",
SUM(rc."base-cost-value") as COSTS,
SUM(rh."base-sales-value") as SALES,
SUM(rh."base-sales-value") - SUM(rc."base-cost-value") as PROFIT
FROM MSN.PUB."rec-chg" rh, PUB."job-hdr" jh, pub."rec-cost" rc
WHERE jh."job-date" between '2013-04-01' and '2013-04-30'
and jh."job-id" = rc."rec-id"
and rc."rec-id" = rh."rec-id"
and jh."grp-id" = '0'
and jh."job-status"<>'D'
and jh."job-no" = '81088'
and rc."charge-type" = rh."charge-type"
Group by jh."job-date", jh."job-disp", jh."job-dept", jh."job-route",rc."charge- type",rh."charge-type"
This is not giving me great results at all and I know I am way off. I just need to be put in the right direction.
Update profit to:
SUM(rh."base-sales-value" - rc."base-cost-value") as PROFIT
And update your group by to:
group by jh."job-id", rc."rec-id", rh."rec-id"
This should give your the desired result (hopefully). Sorry didnt not have time to test it myself. The main focus is on group by, which should be applied on a field that would return multiple results for other fields you want to run the sum on.
Your question appears is a little ambiguous, as to whether you want the results by job or by charge type. In either case, you need to aggregate the results before doing the join. The following query does this at the job level:
SELECT jh."job-date", jh."job-disp", jh."job-dept", jh."job-route",
COSTS, SALES, SALES - COSTS as PROFIT
FROM PUB."job-hdr" jh left outer join
(select rh."rec-id", SUM(rh."base-sales-value") as SALES
from MSN.PUB."rec-chg" rh
group by rh."rec-id"
) rh
on jh."job-id" = rh."rec-id" left outer join
(select rc."rec-id", SUM(rc."base-cost-value") as COSTS
from pub."rec-cost" rc
group by rc."rec-id"
) rc
on jh."job-id" = rc."rec-id"
WHERE jh."grp-id" = '0' and
jh."job-status" <> 'D' and
jh."job-no" = '81088';
Notice that I replaced your implicit join syntax with explicit join syntax. The explicit version is much better, so you should learn to use it.

Convert MySQL query to MS SQL Server ... failing on aggregate requirements

GOAL:
I need to retrieve the most recent message date (max), number of rows in its attachment, and the vendors name.
Also, we need to limit the results to messages sent this year (after 2014-01-01 00:00:00.000) which have an attachment with 50k rows or more.
TRIED:
See this sqlFiddle.
SELECT
v.name
,a.attachmentRows
,MAX(e.createdDate) recentDate
FROM emailMessage e
INNER JOIN vendor v
ON (e.vendorID = v.vendorID)
INNER JOIN emailAttachment a
ON (e.emailMessageID = a.emailMessageID)
WHERE e.createdDate > '2014-01-01 00:00:00.000'
AND a.attachmentRows >= 50000
GROUP BY e.vendorID
EXPECTATIONS:
| NAME | ATTACHMENTROWS | RECENTDATE |
|-------------|----------------|---------------------------------|
| "Company C" | 123880 | February, 22 2014 10:00:00+0000 |
PROBLEM:
While my SQL skills are rather primitive, I'm fairly comfortable with the MySQL flavor so I started my fiddling there. That query worked as expected.
When switching over to SQL Server, though, I run into this error for each of the selected fields:
Column 'blahBlah' is invalid in the select list because it is not contained in either an aggregate function or the GROUP BY clause.
I understand what the error is telling me, but with three tables involved, I'm at a loss as to how to remedy it. (And of course, simply grouping by all the selected fields would not yield the desired results.)
PLEA:
Please help!
Please try this Fiddle:
SELECT
v.name
,a.attachmentRows
,e.createdDate recentDate
FROM emailMessage e
INNER JOIN vendor v
ON (e.vendorID = v.vendorID)
INNER JOIN emailAttachment a
ON (e.emailMessageID = a.emailMessageID)
INNER JOIN (SELECT MAX(emailMessageID) emailMessageID, vendorID from emailMessage group by vendorID) as maxi
on maxi.emailMessageID = e.emailMessageID
WHERE e.createdDate > '2014-01-01 00:00:00.000'
AND a.attachmentRows >= 50000
This assumes the emailMessageID increments with the createdDate. Using the date is problematic if two emails arrive at the exact same time stamp.
SELECT
v.name
,a.attachmentRows
,MAX(e.createdDate) recentDate
FROM emailMessage e
INNER JOIN vendor v
ON (e.vendorID = v.vendorID)
INNER JOIN emailAttachment a
ON (e.emailMessageID = a.emailMessageID)
WHERE e.createdDate > '2014-01-01 00:00:00.000'
AND a.attachmentRows >= 50000
GROUP BY v.name ,a.attachmentRows