Logic to identify new line item for current month - sql

I am trying to write a query to spot new line items appearing in my data set. So for example I have the following table structure.
The logic needs to identify if the line item is new since the previous billedmonth
TableA
So if I was to write it in English.
Select IF 'CLI' & 'Description' & 'UnitCost' doesn't exist for BilledMonth -1
I have managed to create a join showing if it exists for the previous billing month.
But I am really struggling with the negative logic (i.e. the line item is new for this month)
Any help greatly appreciated.

SELECT t.CLI, t.Description
FROM yourTable t
LEFT JOIN yourTable t2
ON t.CLI = t2.CLI
AND t.Description = t2.Description
AND t.UnitCost = t2.UnitCost
AND t.BilledMonth - 1 = t2.BilledMonth
WHERE t2.CLI is null

I think sql server supports analytic functions, so something like this should work:
select CLI, Description, UnitCost, billedMonth
from (
select CLI, Description, UnitCost, billedMonth
count(*) over (partition by CLI, Description, UnitCost order by billedMonth) cnt
from mytable
) where cnt = 1
Iff this works it is very likely to be way more efficient and faster than a join based select statement.

Related

Oracle Query hierarchical level cintaining when case with GROUP BY

I cannot solve a problem with my GROUP BY problem in my query containing CASE...WHEN
Could you help me please with that?
select ID,
CODE,
NOM AS TITLE,
level,
ID_PARENT,
CASE ID_PARENT
WHEN 1111 THEN 'MAIN'
ELSE
(
SUBSTR(
(
SELECT CODE FROM units o
INNER JOIN LIB_UNITS_MV oLab on oLab.ID = o.ID WHERE o.ID = units.ID_PARENT AND LNG_CD = 'ENG'
)
, 7)
)
END AS "PARENT_CODE"
from units
INNER JOIN LIB_UNITS_MV orgLab on orgLab.ID = units.ID
WHERE orgLab.LNG ='ENG'
start with units.id = 1111
connect by prior u.ID = u.ID_PARENT
GROUP BY ID, CODE, NOM, level, ID_PARENT
I obtain the error "not a GROUP BY expression" because I have the WHEN...CASE
Thank you in advance for your help
Regards
Becuase when you group by you need to group by sufficient number of columns, which you use in select statement, outside aggregating functions (min, max, sum etc). So in your case this means - you can either group by all columns used in your case statement, or group by the whole case statement (just copy it over to your group by), or any set of sub-combinations of the whole case, altogether covering it completely. However - since you are not using any aggregate functions I would just do distinct and drop group by altogether.

Conditionally use CASE...WHEN - Oracle SQL

I have two tables like so:
tblOrders: OrderNo (pk), CurrentStepNo (fk)
tblSteps: StepNo (pk), OrderNo (fk), StepName, StepType, StepStart, StepStop
tblOrders contains tons of information about our sales orders, while tblSteps contains tons of information regarding the proper sequential steps it takes to build the material we are selling.
I am trying to construct a query that follows this logic:
"For all orders, select the current step name from the step table. If
the Step Type is equal to 'XO', then select the most recently
completed (where StepStop is not null) regular step (where StepStop is
equal to 'YY')"
I have the following query:
SELECT
tblOrders.*,
tblSteps.StepName
FROM
tblOrders
INNER JOIN tblSteps
ON tblOrders.OrderNo = tblSteps.OrderNo
AND tblOrders.CurrentStepNo = tblSteps.StepNo
Which successfully returns to me the current step name for an in-process order. What I need to achieve is, when the tblOrders.CurrentStepNo is of type 'XO', to find the MAX(tblSteps.StepStop) WHERE tblSteps.StepType = 'YY'. However, I am having trouble putting that logic into my already working query.
Note: I am sorry for the lack of sample data in this example. I would normally post but cannot in this instance. This is also not a homework question.
I have reviewed these references:
Case in Select Statement
https://blogs.msdn.microsoft.com/craigfr/2006/08/23/subqueries-in-case-expressions/
But no luck so far.
I have tried this:
SELECT
tblOrders.*,
CASE
WHEN tblSteps.StepType = 'XO' THEN (-- Some logic here)
ELSE tblSteps.StepName
END AS StepName
FROM
tblOrders
INNER JOIN tblSteps
ON tblOrders.OrderNo = tblSteps.OrderNo
AND tblOrders.CurrentStepNo = tblSteps.StepNo
But am struggling to properly formulate the logic
Join all steps, rank them with ROW_NUMBER, and stay with the best ranked:
select *
from
(
select
o.*,
s.*,
row_number() over
(partition by o.orderno
order by case when s.steptype <> 'XO' and s.stepno = o.currentstepno then 1
when s.steptype <> 'YY' then 2
else 3 end, s.stepstop desc nulls last) as rn
from tblorders o
join tblsteps s on s.orderno = o.orderno
) ranked
where rn = 1
order by orderno;

DB2 getting QDT Array List maximum exceeded using CTE and sql recursion

I am using CTE to create a recursive query to merge multiple column data into one.
I have about 9 working CTE's (I need to merge columns a few times in one row per request, so I have the CTE helpers). When I add the 10th, I get an error. I am running the query on Visual Studio 2010 and here is the error:
And on the As400 system using the, WRKOBJLCK MyUserProfile *USRPRF command, I see:
I can't find any information on this.
I am using DB2 running on an AS400 system, and using: Operating system: i5/OS Version: V5R4M0
I repeat these same 3 CTE's but with different conditions to compare against:
t1A (ROWNUM, PARTNO, LOCNAM, LOCCODE, QTY) AS
(
SELECT rownumber() over(partition by s2.LOCPART), s2.LOCPART, s2.LOCNAM, s2.LOCCODE, s2.LOCQTY
FROM (
SELECT distinct s1.LOCPART, L.LOCNAM, L.LOCCODE, L.LOCQTY
FROM(
SELECT COUNT(LOCPART) AS counts, LOCPART
FROM LOCATIONS
WHERE LOCCODE = 'A'
GROUP BY LOCPART) S1, LOCATIONS L
WHERE S1.COUNTS > 1 AND S1.LOCPART = L.LOCPART AND L.LOCCODE = 'A'
)s2
),
t2A(PARTNO, LIST, QTY, CODE, CNT) AS
(
select PARTNO, LOCNAM, QTY, LOCCODE, 1
from t1A
where ROWNUM = 1
UNION ALL
select t2A.PARTNO, t2A.LIST || ', ' || t1A.LOCNAM, t1A.QTY, t1A.LOCCODE, t2A.CNT + 1
FROM t2A, t1A
where t2A.PARTNO = t1A.PARTNO
AND t2A.CNT + 1 = t1A.ROWNUM
),
t3A(PARTNO, LIST, QTY, CODE, CNT) AS
(
select t2.PARTNO, t2.LIST, q.SQTY, t2.CODE, t2.CNT
from(
select SUM(QTY) as SQTY, PARTNO
FROM t1A
GROUP BY PARTNO
) q, t2A t2
where t2.PARTNO = q.PARTNO
)
Using these, I just call a simple select on one of the CTE's just for testing, and I get the error each time when I have more than 9 CTE's (even if only one is being called).
In the AS400 error (green screen snapshot) what does QDT stand for, and when am I using an Array here?
This was a mess. Error after error. The only way I could get around this was to create views and piece them together.
When creating the view I was only able to get it to work with one CTE not multiple, then what worked fine as one recursive CTE, wouldn't work when trying to define as a view. I had to break apart the sub query into views, and I couldn't create a view out of SELECT rownumber() over(partition by COL1, Col2) that contained a sub query, I had to break it down into two views. If I called SELECT rownumber() over(partition by COL1, Col2) using a view as its subquery and threw that into the CTE it wouldn't work. I had to put the SELECT rownumber() over(partition by COL1, Col2) with its inner view into another view, and then I was able to use it in the CTE, and then create a main view out of all of that.
Also, Each error I got was a system error not SQL.
So in conclusion, I relied heavily on views to fix my issue if anyone ever runs across this same problem.

SSRS Table of locations per item type

I have a basic query which shows what the latest product to be put in each location (FVTank) is:
SELECT TOP 1
T0.[DateTime],
T0.[TankName],
T1.[Item]
FROM
t005_pci_data T0
INNER JOIN t001_fvbatch T1 ON T1.[FVBatch] = T0.[FVBatch]
WHERE
T0.[TankName] = 'FV101'
UNION
SELECT TOP 1
T0.[DateTime],
T0.[TankName],
T1.[Item]
FROM
t005_pci_data T0
INNER JOIN t001_fvbatch T1 ON T1.[FVBatch] = T0.[FVBatch]
WHERE
T0.[TankName] = 'FV102'
[...etc...]
ORDER BY
T0.[DateTime] DESC
Which gives a result like this:
What I'd like to do is create a summary page on SSRS which would display all the locations which currently hold each item. Ideally it would look something like this:
There are 50 locations and 7 main items so I need it to have 8 headers (one additional one for "other".)
Is there a way to do this in SSRS? Or is there a better solution by doing it in SQL?
Thank you.
Add an additional column to your dataset that calculates a row number for each Item, ordered by the DateTime field:
row_number() over (partition by Item order by DateTime desc) as rn
Judging by your source query in your question, this may be best included as a wrapping select around your final query:
select DateTime
,TankName
,Item
,row_number() over (partition by Item order by DateTime desc) as rn
from(
<Your original query here>
) a
You can then use this as your row group, as without one you will not get the top aligned format you are after in each Item x column. Remember to delete the rn column but keep the grouping:
When you run this report you will get the following format (I didn't bother typing out all your data into my dataset query, hence the missing values):

distinct value per column

I am looking at a report on policy exceptions based on various criteria such as Beacon Score, Debt to Income, and Loan to Value. This information is kept in multiple different tables, and right now the Loan to Value column is causing multiple entries in my report because a specific loan might have multiple pieces of collateral. For proper exception monitoring, I only need one entry.
With all that said, how might I execute the following code, with a distinct value for dbo.Folders.Id? Just putting 'DISTINCT' after the SELECT statement does not seem to work. (Sensitive values masked with '#'.)
SELECT dbo.Folders.LoanOfficerId,
dbo.Folders.Id,
dbo.CollateralType.Description,
dbo.Customers.CUSTNAME,
dbo.Folders.DateLoanActivated,
dbo.Folders.CurrentAccountBalance,
dbo.Folders.UnadvancedCommitAmount,
dbo.Folders.BeaconScore,
dbo.Folders.DebtToIncome,
dbo.Collateral.LoanToValue
FROM dbo.Folders
INNER JOIN dbo.Customers
ON dbo.Folders.CustomersNAMEKEY = dbo.Customers.NAMEKEY
INNER JOIN dbo.Collateral
ON dbo.Folders.Id = dbo.Collateral.FoldersID
INNER JOIN dbo.CollateralType
ON dbo.Collateral.CollateralTypeCollCode = dbo.CollateralType.CollCode
WHERE ( (dbo.Folders.BeaconScore < ###)
AND (dbo.Folders.BeaconScore > ###)
AND (dbo.Folders.CloseCode = 'O')
AND (dbo.Folders.CollateralCode <> ##)
)
OR ( (dbo.Folders.CloseCode = 'O')
AND (dbo.Folders.CustomerType <> '###')
AND (dbo.Folders.CustomerType <> '###')
AND (dbo.Folders.DebtToIncome > ##)
)
OR ( (dbo.Folders.CloseCode = 'O')
AND (dbo.Folders.CustomerType = '###')
AND (dbo.Folders.DebtToIncome > ##)
)
OR ( (dbo.Folders.CloseCode = 'O')
AND (dbo.Folders.CustomerType = '###')
AND (dbo.Folders.DebtToIncome > ##)
)
OR (dbo.Collateral.LoanToValue > dbo.CollateralType.LTV)
Any constructive criticism on my code is welcome. (Static values in the above statement are on the docket to be corrected later with a thresholds/criteria table.) From what I have seen, others have suggested using ROW_COUNT() with PARTITION, but I am unable to make the syntax work.
Comment about formatting: learn to use table aliases. They make the query easier to read and write.
If you only need one row from the results, you can use row_number(). This enumerates the rows for each folder (in your case) and you would just use the first one. You can do this using:
with t as (
<your query here>
)
select t.*
from (select t.*,
row_number() over (partition by FoldersId order by (select NULL)) as seqnum
from t
) t
where seqnum = 1;
On the other hand, if you needed to aggregate information from the collateral tables, then you would use group by in your query with the appropriate aggregation functions.