Creating new column using nvl - sql

here is my problem. I have an inner query that is working fine, however when I pass the results to outer query I get an error. What I want to accomplish is this, on the outer query, I want to always check if this column (GCO_AD_KNOWLEDGE_TEST_SCORE ) has a value, if it has a value I want to alias it as psi score new column name, additionally if this column does not have a value, I want to check for the second column (PSI_OVERALL_SCORE) if it has a value I want to alias it as PSI Score (new column). anyone want to take stab at this, I will appreciate it.
SELECT
NVL ((CASE WHEN BB.GCO_AD_KNOWLEDGE_TEST_SCORE IS NOT NULL THEN BB.GCO_AD_KNOWLEDGE_TEST_SCORE, ) BB.PSI_OVERALL_SCORE) as PSI SCORE
else null
end
FROM(
SELECT
PARENT_PI_NUMBER,PI_CANDIDATE_NUM,REQUEST_LAST_MODIFIED_DT,Contest_number,
MAX(AA.GCO_AD_KNOWLEDGE_TEST_SCORE) KEEP (DENSE_RANK LAST ORDER BY AA.ARANK) MAX_GCO_AD_KNOWLEDGE_TEST_SCORE,
MAX(AA.PSI_OVERALL_SCORE) KEEP (DENSE_RANK LAST ORDER BY AA.ARANK) MAX_PSI_OVERALL_SCORE
FROM (
select
RANK() OVER (PARTITION BY PARENT_PI_NUMBER ORDER BY REQUEST_LAST_MODIFIED_DT) ARANK
,Contest_number,PARENT_PI_NUMBER,PI_CANDIDATE_NUM,PI_NUMBER,PSI_OVERALL_SCORE,GCO_AD_KNOWLEDGE_TEST_SCORE,REQUEST_LAST_MODIFIED_DT
from
WC_APPLICATION_EVENT_F
WHERE GCO_AD_KNOWLEDGE_TEST_SCORE != '10100'
--WHERE PI_CANDIDATE_NUM = '4639022'and GCO_AD_KNOWLEDGE_TEST_SCORE is not null
) AA
GROUP BY PARENT_PI_NUMBER,PI_CANDIDATE_NUM,REQUEST_LAST_MODIFIED_DT,Contest_number;
--ORDER By PARENT_PI_NUMBER,PI_NUMBER,REQUEST_LAST_MODIFIED_DT;
) BB;

Your inner query still has a semicolon at the end.
MX_GCO_AD_KNOWLEDGE_TEST_SCORE is 31 characters so isn't valid unless you're on 12cR2 (or higher) with long identifiers enabled.
Your nvl/case construction has parentheses and end in the wrong places and has a spare comma with nothing following it.
Your nvl/case are attempting to refer to the original GCO_AD_KNOWLEDGE_TEST_SCORE and PSI_OVERALL_SCORE columns, but those are not exposed by the inline view.
It sounds like you're trying to get:
COALESCE(BB.MX_GCO_AD_KNOWLEDGE_TEST_SCORE, BB.MX_PSI_OVERALL_SCORE) as PSI_SCORE
using the aliases from the inner query, which I've modified from MAX_ to MX_ to make the first one legal.
It's hard to tell if the inner query is actually right with data and expected results, but you said that it's doing what you want.

Related

How to select another column in table if first column doesnt have value that i need

I have 1 table where is 2 columns that i need to use at this moment( length and length_to_fault). If length has a null(N/A) value then i need to select value from length_to_fault and opposite.
I also need to sort everything and i can do it with 1 column like this:
select d.* from (select d.*, lead(length::float) over (partition by port_nbr, pair order by add_date) as next_length from diags d where length != 'N/A') d
this select sorts everything except length_to_fault. If 1 record has value in length_to_fault , it will be ignored and it wont show up.
Is there any suggestions?
Maybe its possible to just make these 2 columns into 1? It sounds much logical. But how?
I changed it to select d.* from (select d.*, lead(sum(length::float + length_to_fault::float)) over (partition by port_nbr, pair order by d.add_date) as next_length from diags d)d
i get error : column "d.ip" must appear in the group by clause or be used in an aggregate function.
I dont need to use ip column... I even dont know where to put that ip right now

Eliminating Entries Based On Revision

I need to figure out how to eliminate older revisions from my query's results, my database stores orders as 'Q000000' and revisions have an appended '-number'. My query currently is as follows:
SELECT DISTINCT Estimate.EstimateNo
FROM Estimate
INNER JOIN EstimateDetails ON EstimateDetails.EstimateID = Estimate.EstimateID
INNER JOIN EstimateDoorList ON EstimateDoorList.ItemSpecID = EstimateDetails.ItemSpecID
WHERE (Estimate.SalesRepID = '67' OR Estimate.SalesRepID = '61') AND Estimate.EntryDate >= '2017-01-01 00:00:00.000' AND EstimateDoorList.SlabSpecies LIKE '%MDF%'
ORDER BY Estimate.EstimateNo
So for instance, the results would include:
Q120455-10
Q120445-11
Q121675-2
Q122361-1
Q123456
Q123456-1
From this, I need to eliminate 'Q120455-10' because of the presence of '-11' for that order, and 'Q123456' because of the presence of the '-1' revision. I'm struggling greatly with figuring out how to do this, my immediate thought was to use case statements but I'm not sure what is the best way to implement them and how to filter. Thank you in advance, let me know if any more information is needed.
First you have to parse your EstimateNo column into sequence number and revision number using CHARINDEX and SUBSTRING (or STRING_SPLIT in newer versions) and CAST/CONVERT the revision to a numeric type
SELECT
SUBSTRING(Estimate.EstimateNo,0,CHARINDEX('-',Estimate.EstimateNo)) as [EstimateNo],
CAST(SUBSTRING(Estimate.EstimateNo,CHARINDEX('-',Estimate.EstimateNo)+1, LEN(Estimate.EstimateNo)-CHARINDEX('-',Estimate.EstimateNo)+1) as INT) as [EstimateRevision]
FROM
...
You can then use
APPLY - to select TOP 1 row that matches the EstimateNo or
Window function such as ROW_NUMBER to select only records with row number of 1
For example, using a ROW_NUMBER would look something like below:
SELECT
ROW_NUMBER() OVER(PARTITION BY EstimateNo ORDER BY EstimateRevision DESC) AS "LastRevisionForEstimate",
-- rest of the needed columns
FROM
(
-- query above goes here
)
You can then wrap the query above in a simple select with a where predicate filtering out a specific value of LastRevisionForEstimate, for instance
SELECT --needed columns
FROM -- result set above
WHERE LastRevisionForEstimate = 1
Please note that this is to a certain extent, pseudocode, as I do not have your schema and cannot test the query
If you dislike the nested selects, check out the Common Table Expressions

CASE Clause on select clause throwing 'SQLCODE=-811, SQLSTATE=21000' Error

This query is very well working in Oracle. But it is not working in DB2. It is throwing
DB2 SQL Error: SQLCODE=-811, SQLSTATE=21000, SQLERRMC=null, DRIVER=3.61.65
error when the sub query under THEN clause is returning 2 rows.
However, my question is why would it execute in the first place as my WHEN clause turns to be false always.
SELECT
CASE
WHEN (SELECT COUNT(1)
FROM STOP ST,
FACILITY FAC
WHERE ST.FACILITY_ID = FAC.FACILITY_ID
AND FAC.IS_DOCK_SCHED_FAC=1
AND ST.SHIPMENT_ID = 2779) = 1
THEN
(SELECT ST.FACILITY_ALIAS_ID
FROM STOP ST,
FACILITY FAC
WHERE ST.FACILITY_ID = FAC.FACILITY_ID
AND FAC.IS_DOCK_SCHED_FAC=1
AND ST.SHIPMENT_ID = 2779
)
ELSE NULL
END STAPPFAC
FROM SHIPMENT SHIPMENT
WHERE SHIPMENT.SHIPMENT_ID IN (2779);
The SQL standard does not require short cut evaluation (ie evaluation order of the parts of the CASE statement). Oracle chooses to specify shortcut evaluation, however DB2 seems to not do that.
Rewriting your query a little for DB2 (8.1+ only for FETCH in subqueries) should allow it to run (unsure if you need the added ORDER BY and don't have DB2 to test on at the moment)
SELECT
CASE
WHEN (SELECT COUNT(1)
FROM STOP ST,
FACILITY FAC
WHERE ST.FACILITY_ID = FAC.FACILITY_ID
AND FAC.IS_DOCK_SCHED_FAC=1
AND ST.SHIPMENT_ID = 2779) = 1
THEN
(SELECT ST.FACILITY_ALIAS_ID
FROM STOP ST,
FACILITY FAC
WHERE ST.FACILITY_ID = FAC.FACILITY_ID
AND FAC.IS_DOCK_SCHED_FAC=1
AND ST.SHIPMENT_ID = 2779
ORDER BY ST.SHIPMENT_ID
FETCH FIRST 1 ROWS ONLY
)
ELSE NULL
END STAPPFAC
FROM SHIPMENT SHIPMENT
WHERE SHIPMENT.SHIPMENT_ID IN (2779);
Hmm... you're running the same query twice. I get the feeling you're not thinking in sets (how SQL operates), but in a more procedural form (ie, how most common programming languages work). You probably want to rewrite this to take advantage of how RDBMSs are supposed to work:
SELECT Current_Stop.facility_alias_id
FROM SYSIBM/SYSDUMMY1
LEFT JOIN (SELECT MAX(Stop.facility_alias_id) AS facility_alias_id
FROM Stop
JOIN Facility
ON Facility.facility_id = Stop.facility_id
AND Facility.is_dock_sched_fac = 1
WHERE Stop.shipment_id = 2779
HAVING COUNT(*) = 1) Current_Stop
ON 1 = 1
(no sample data, so not tested. There's a couple of other ways to write this based on other needs)
This should work on all RDBMSs.
So what's going on here, why does this work? (And why did I remove the reference to Shipment?)
First, let's look at your query again:
CASE WHEN (SELECT COUNT(1)
FROM STOP ST, FACILITY FAC
WHERE ST.FACILITY_ID = FAC.FACILITY_ID
AND FAC.IS_DOCK_SCHED_FAC = 1
AND ST.SHIPMENT_ID = 2779) = 1
THEN (SELECT ST.FACILITY_ALIAS_ID
FROM STOP ST, FACILITY FAC
WHERE ST.FACILITY_ID = FAC.FACILITY_ID
AND FAC.IS_DOCK_SCHED_FAC = 1
AND ST.SHIPMENT_ID = 2779)
ELSE NULL END
(First off, stop using the implicit-join syntax - that is, comma-separated FROM clauses - always explicitly qualify your joins. For one thing, it's way too easy to miss a condition you should be joining on)
...from this it's obvious that your statement is the 'same' in both queries, and shows what you're attempting - if the dataset has one row, return it, otherwise the result should be null.
Enter the HAVING clause:
HAVING COUNT(*) = 1
This is essentially a WHERE clause for aggregates (functions like MAX(...), or here, COUNT(...)). This is useful when you want to make sure some aspect of the entire set matches a given criteria. Here, we want to make sure there's just one row, so using COUNT(*) = 1 as the condition is appropriate; if there's more (or less! could be 0 rows!) the set will be discarded/ignored.
Of course, using HAVING means we're using an aggregate, the usual rules apply: all columns must either be in a GROUP BY (which is actually an option in this case), or an aggregate function. Because we only want/expect one row, we can cheat a little, and just specify a simple MAX(...) to satisfy the parser.
At this point, the new subquery returns one row (containing one column) if there was only one row in the initial data, and no rows otherwise (this part is important). However, we actually need to return a row regardless.
FROM SYSIBM/SYSDUMMY1
This is a handy dummy table on all DB2 installations. It has one row, with a single column containing '1' (character '1', not numeric 1). We're actually interested in the fact that it has only one row...
LEFT JOIN (SELECT ... )
ON 1 = 1
A LEFT JOIN takes every row in the preceding set (all joined rows from the preceding tables), and multiplies it by every row in the next table reference, multiplying by 1 in the case that the set on the right (the new reference, our subquery) has no rows. (This is different from how a regular (INNER) JOIN works, which multiplies by 0 in the case that there is no row) Of course, we only maybe have 1 row, so there's only going to be a maximum of one result row. We're required to have an ON ... clause, but there's no data to actually correlate between the references, so a simple always-true condition is used.
To get our data, we just need to get the relevant column:
SELECT Current_Stop.facility_alias_id
... if there's the one row of data, it's returned. In the case that there is some other count of rows, the HAVING clause throws out the set, and the LEFT JOIN causes the column to be filled in with a null (no data) value.
So why did I remove the reference to Shipment? First off, you weren't using any data from the table - the only column in the result set was from the subquery. I also have good reason to believe that there would only be one row returned in this case - you're specifying a single shipment_id value (which implies you know it exists). If we don't need anything from the table (including the number of rows in that table), it's usually best to remove it from the statement: doing so can simplify the work the db needs to do.

Find related "ordered pairs" in SQL

Let's say I have a table format that looks exactly like this:
I'd like to write a query that locates the maximum station for a given frame and output case (results are grouped by frame & output case) but also return the ordered P (& eventually V2, V3, T, M2 & M3) that would be associated with the maximum station. The desired query is shown below:
I can't for the life of me figure this out. I've posted a copy of the access database to my google drive: https://drive.google.com/folderview?id=0B9VpkDoFQISJOFcwS2RMSGJ5RVk&usp=sharing
select x.*, t.p
from (select frame, outputcase, max(station) as max_station
from tbl
group by frame, outputcase) x
inner join tbl t
on x.frame = t.frame
and x.outputcase = t.outputcase
and x.max_station = t.station
order by x.frame, x.outputcase;
Just as a note to avoid confusion, w/ that second column, t is the table alias, p is the column name.
The subquery, which I've assigned an alias of x, finds the max(station) for each unique combination of (frame, outputcase). That is what you want, but the problem does not stop there, you also want column p. The reason that couldn't be selected in the same query is because you would have had to group by it, and you don't want the max(station) for each combination of (frame, outputcase, p). You want the max(station) for each combination of (frame, outputcase).
Because we couldn't get column p in that first step, we have to join back to the original table using the value we obtained (which I've assigned an alias, max_station), and the obvious join conditions of frame and outputcase. So we join back to the original table on those 3 things, 2 of which are fields on the actual table, one of which was calculated in the subquery (max_station).
Because we've joined back to the original table, we can then select column p from the original table.
Takes a bit to return the query, but the result below provides the desired result:
SELECT t1.*
FROM [Element Forces - Frames] as t1
WHERE t1.Station In (SELECT TOP 1 t2.Station
FROM [Element Forces - Frames] as t2
WHERE t2.Frame = t1.Frame
ORDER BY t2.Station DESC)
ORDER BY t1.Frame ASC, t1.OutputCase ASC;
I still want to thank everyone who posted answers. I'm sure it's just syntax errors on my part that I was struggling with.

designedly big value in SQL

Ok, I have to put null values last. The query should run under Oracle and MySQL.
I've already came up with
ORDER BY
CASE WHEN some_table.ord IS NULL THEN 9999999999 ELSE some_table.ord END
I should use value > max(some_table.ord) instead of 9999999999.
I think subquery to determine this value is too ugly here.
If this was C++ I can use some macro like INT_MAX for this purpose. Can you name its cross-DBMS SQL twin?
UPDATE
the question is if can I put something .. beautiful instead of 9999999999, so that query will work both in Oracle and MySQL,
not how to put null values last
Use an extra column for the null flag:
order by
case when some_table.ord is null then 2 else 1 end ,
some_table.ord
Or, if you have enough knowledge of the values that this column can take, just hard-code a number that is larger than anything in there:
order by coalesce(some_table.ord, 9999999999)
In Oracle, it's simply
ORDER BY some_table.ord NULLS LAST
Something like the following might work:
SELECT S.VAL1, S.VAL2, S.VAL3, COALESCE(S.ORD, O.MAX_ORD+1) AS ORD
FROM SOME_TABLE S,
(SELECT MAX(ORDER) AS MAX_ORD FROM SOME_TABLE) O
WHERE S.whatever = whichever AND
S.something <> something_else
ORDER BY ORD
Not sure if MySQL allows sub-queries in the FROM list. The idea here is to avoid the use of a magic value to handle the NULL case.
Share and enjoy.