Return duplicate min values - oracle

See the table below for reference. I need to return duplicate rows containing the min value.
In this example I want to show only the 2 rows that have SLAT_LEN = 30 or rather the min SLAT_LEN. I tried rank but when I do it does a consecutive rank. I want the duplicate sizes to have the same rank and to rank consecutively by size.
The sizes change so I can't just use a condition like SLAT_LEN = 30.
Or is there a different approach that I should take?
select *
from(
select lg.wd_demand_id,wm.slat_len, wm.prof_size, wm.wd_material_id, wm.color, rank() over ( partition by wm.slat_len order by lg.wd_demand_id) as rank
from wd_demand_log lg, wd_bins wb, wd_material wm, wd_bins_material wbm
where lg.wd_bins_id = wb.wd_bins_id
and lg.wd_material_id = wm.wd_material_id
and lg.wd_bins_id = wbm.wd_bins_id
and lg.wd_material_id = wbm.wd_material_id
AND lg.plant_id = 44 AND lg.dept_id = 220 AND wb.plant_id = 44 AND wb.dept_id = 220
AND NOT EXISTS(SELECT dmpln.wd_demand_id FROM wd_demnd_pln_inv dmpln WHERE dmpln.wd_demand_id = lg.wd_demand_id)
AND wm.prof_size = '2' AND wm.color = 450
AND lg.wd_po_error is null)

You'd use PARTITION BY to give the range in which to rank. The ORDER BY specifies the ranking. So in your case you'll need something like
rank() over (order by wm.slat_len)
In case you have Oracle 12c you can limit your results with FETCH FIRST:
ORDER BY wm.slat_len
FETCH FIRST 1 ROW WITH TIES;

Perhaps I am not understanding the problem correctly, but here is my potential solution:
WITH aset
AS (SELECT lg.wd_demand_id
, wm.slat_len
, wm.prof_size
, wm.wd_material_id
, wm.color
FROM wd_demand_log lg, wd_bins wb, wd_material wm
, wd_bins_material wbm
WHERE lg.wd_bins_id = wb.wd_bins_id
AND lg.wd_material_id = wm.wd_material_id
AND lg.wd_bins_id = wbm.wd_bins_id
AND lg.wd_material_id = wbm.wd_material_id
AND lg.plant_id = 44
AND lg.dept_id = 220
AND wb.plant_id = 44
AND wb.dept_id = 220
AND NOT EXISTS
(SELECT dmpln.wd_demand_id
FROM wd_demnd_pln_inv dmpln
WHERE dmpln.wd_demand_id = lg.wd_demand_id)
AND wm.prof_size = '2'
AND wm.color = 450
AND lg.wd_po_error IS NULL)
SELECT *
FROM aset
WHERE slat_len = (SELECT MIN (slat_len)
FROM aset)

Related

SQL Getting the total or sum of a column

So here's the query that I currently have.
`SELECT war.id, inv.pro_id, inv.quantity
FROM L4_Warehouses war, L4_Inventories inv, L4_Employees emp, L4_Orders ord
WHERE inv.war_id = war.id AND
war.id = emp.war_id AND
emp.id = ord.emp_id AND
ord.status = 'P'
ORDER BY inv.war_id, inv.pro_id;`
this query gives this table.
Now, what I want to get for the final result is to get the sum of those Quantities, example below
ID PRO_ID QUANTITY
1 100 18
1 101 6
1 110 6
Try SUM and GROUP BY:
SELECT war.id, inv.pro_id, SUM(inv.quantity)
FROM L4_Warehouses war, L4_Inventories inv, L4_Employees emp, L4_Orders ord
WHERE inv.war_id = war.id AND
war.id = emp.war_id AND
emp.id = ord.emp_id AND
ord.status = 'P'
GROUP BY war.id, inv.pro_id
ORDER BY inv.war_id, inv.pro_id;

Performance Issue - Join/Index

I have this query:
SELECT *
FROM (SELECT *
FROM jmr_bbi_dife_vendas_neg2 dife
WHERE dife.processado = 'N'
AND dife.removido = 'N'
AND dife.revertido = 'N'
AND TO_DATE(LPAD(dife.DAY, 2, '0') || LPAD(dife.MONTH, 2, '0') || dife.YEAR, 'DDMMYYYY') <= to_date('20160331','YYYYMMDD')
) dife
LEFT JOIN jmr_bbi_dade_vendas dade
ON dade.codins = 1
AND dade.codneg = 2
AND dade.tipdoc_vnd = dife.doc_type
AND dade.numdoc_vnd = dife.doc_no
AND dade.codart = dife.item
AND dade.anoper = dife.year
AND dade.mesper = dife.month;
and i create index on DADE.
Index
But when i execute the explain plan on this query, the index is not used:
Explain Plan
I already execute the gather statistics of full table, but don't work either.
Can help me please?
This two tables: DIFE and DADE are 2 tables example.
My original table of each other have millions of rows.
Thank you!
Create index on columns of both table which are being used in join-condition.
Also move dade.codins = 1 AND dade.codneg = 2 into where condition

rownum and group by to limit to 1000 rows in detail

I need to limit to 1000 rows in detail table EL8_STGG_CEHL_EXCP for each unique combination keys(AST_ID,PRCS_ID,PRCS_NME,PRCS_STEP_NME,SRC_APPL_LOG_DT) in the EL8_STGG_CEHL_LOG.
For example - If there are 3 unique combination keys, I expect 3000 rows in the detail table.
SELECT
E.AST_ID,
E.PRCS_ID,
E.PRCS_NME,
E.PRCS_STEP_NME,
E.SRC_APPL_LOG_DT
FROM EL8_STGG_CEHL_EXCP E,EL8_STGG_CEHL_LOG L
WHERE
L.AST_ID = E.AST_ID
AND L.PRCS_ID = E.PRCS_ID
AND L.PRCS_NME = E.PRCS_NME
AND L.PRCS_STEP_NME = E.PRCS_STEP_NME
AND L.SRC_APPL_LOG_DT = E.SRC_APPL_LOG_DT
AND (L.CEHL_PICK_UP_IND IS NULL OR UPPER(L.CEHL_PICK_UP_IND) not in ('Y','P'))
GROUP BY
E.AST_ID,
E.PRCS_ID,
E.PRCS_NME,
E.PRCS_STEP_NME,
E.SRC_APPL_LOG_DT
it would be great help if anyone can help me out
Thanks
Untested... that's a lot of data to mock up.
Basically, I use my favorite analytic function, row_number()
with limited as (
select
L.*,
row_number() over (partition by AST_ID,PRCS_ID,PRCS_NME,
PRCS_STEP_NME,SRC_APPL_LOG_DT order by rownum) as rn
from EL8_STGG_CEHL_LOG L
)
SELECT
E.AST_ID,
E.PRCS_ID,
E.PRCS_NME,
E.PRCS_STEP_NME,
E.SRC_APPL_LOG_DT
FROM
EL8_STGG_CEHL_EXCP E,
limited L
WHERE
L.AST_ID = E.AST_ID
AND L.PRCS_ID = E.PRCS_ID
AND L.PRCS_NME = E.PRCS_NME
AND L.PRCS_STEP_NME = E.PRCS_STEP_NME
AND L.SRC_APPL_LOG_DT = E.SRC_APPL_LOG_DT
AND L.RN <= 1000
AND (L.CEHL_PICK_UP_IND IS NULL OR UPPER(L.CEHL_PICK_UP_IND) not in ('Y','P'))

Query to get list of Inventory Items for which there is no material transaction in Oracle

Have a small doubt I want to compile a SQL query in Inventory where I have to get those Items for which transactions have not been recorded during a period of at least a specified number of days.
The days could be 30 days or 2 months depends. So I want to get those items for which no transaction was recorded for lets say 30 days. Could anyone give me an idea of how to go about this thing?? I am using r12. I came up with the following query but it is giving many records. The commented portions of this query remains commented only
select distinct msi.segment1, msi.description, msi.primary_uom_code,
msi.inventory_item_id
from mtl_system_items_b msi /*,
mtl_material_transactions mmt*/
where /*msi.inventory_item_id = mmt.inventory_item_id
AND msi.organization_id = mmt.organization_id
AND NVL((SELECT SUM(transaction_quantity)
FROM mtl_onhand_quantities
WHERE inventory_item_id = msi.inventory_item_id),
0) = 0
AND TRUNC(mmt.transaction_date) <= SYSDATE - &D
AND*/
not exists
(select *
from mtl_material_transactions mmt
where msi.inventory_item_id = mmt.inventory_item_id
and msi.organization_id = mmt.organization_id
and trunc(mmt.transaction_date) < sysdate - &D)
Here is a variation of your query that will give all items with on hand inventory that have not been transacted in a determined number of days.
select distinct msi.segment1
,msi.description
,msi.primary_uom_code
--,msi.inventory_item_id
,q.organization_id
,q.quantity
from mtl_system_items_b msi
join (SELECT inventory_item_id, organization_id, SUM(transaction_quantity) quantity
FROM mtl_onhand_quantities
GROUP BY inventory_item_id, organization_id) q on msi.inventory_item_id = q.inventory_item_id and msi.organization_id=q.organization_id
where not exists (select *
from mtl_material_transactions mmt
where msi.inventory_item_id = mmt.inventory_item_id
and msi.organization_id = mmt.organization_id
and trunc(mmt.transaction_date) > sysdate - 180)
order by q.organization_id,msi.segment1;

SSRS How to get the first and last values of a matrix row group?

I basically have the screenshot below as my layout.
My matrix columns are DCG1 and DCG2.
At the end of the Matrix I have a Total Group, which works just find. But I want to find the difference between my first and last value of the group. I've tried everything from ReportItems! to values. I cannot get SSRS to recognize these values.
So basically in the screen shots below. Screen shot 1 is the matrix structure. I have a column group called Test1, I want the first Value of Test1 and the last Value of Test 1 and place that in the Red box.
In screenshot 2, you can see the values i want to compare. The table groupings are named the same as the column + group. So dcs1group/dcs2group
Okay here is the DDL and DML for the datasource
http://pastebin.com/1ySN701D
The pastebin has been removed. Why, not sure so here it is below.
IF EXISTS
(SELECT [name]
FROM tempdb.sys.tables
WHERE [name] LIKE '%tmpHoldingTable%')
BEGIN
DROP TABLE #tmpHoldingTable;
END;
CREATE TABLE #tmpHoldingTable
(
dcs1 NVARCHAR (50),
dcs2 NVARCHAR (50),
Total DECIMAL (10, 2),
Test1 NVARCHAR (50)
)
INSERT INTO #tmpHoldingTable (dcs1,
dcs2,
Total,
Test1)
VALUES ('Contract',
'Breach of Contract',
500.00,
'01/01/2013-12/31/2013'),
('Contract',
'Breach of Contract',
300.00,
'01/01/2014-12/31/2014'),
('Employment',
'Discrimination',
500.00,
'01/01/2013-12/31/2013'),
('Employment',
'Discrimination',
300.00,
'01/01/2014-12/31/2014'),
('Employment',
'Research',
500.00,
'01/01/2013-12/31/2013'),
('Employment',
'Research',
300.00,
'01/01/2014-12/31/2014')
SELECT * FROM #tmpHoldingTable;
Yes, this is possible, but as you can see it is a bit complicated.
To make this a more generic answer, I have created my own DataSet, with simplified columns but more data:
select grp = 1, val = 100, dt = cast('01-jan-2015' as date)
union all select grp = 1, val = 110, dt = cast('01-jan-2015' as date)
union all select grp = 1, val = 200, dt = cast('02-jan-2015' as date)
union all select grp = 1, val = 210, dt = cast('02-jan-2015' as date)
union all select grp = 1, val = 300, dt = cast('03-jan-2015' as date)
union all select grp = 1, val = 310, dt = cast('03-jan-2015' as date)
union all select grp = 1, val = 400, dt = cast('04-jan-2015' as date)
union all select grp = 1, val = 410, dt = cast('04-jan-2015' as date)
union all select grp = 1, val = 500, dt = cast('05-jan-2015' as date)
union all select grp = 1, val = 510, dt = cast('05-jan-2015' as date)
union all select grp = 2, val = 220, dt = cast('02-jan-2015' as date)
union all select grp = 2, val = 230, dt = cast('02-jan-2015' as date)
union all select grp = 2, val = 320, dt = cast('03-jan-2015' as date)
union all select grp = 2, val = 330, dt = cast('03-jan-2015' as date)
union all select grp = 2, val = 420, dt = cast('04-jan-2015' as date)
union all select grp = 2, val = 430, dt = cast('04-jan-2015' as date)
Note that each grp / dt combination has two values, and that grp 1 is over a longer range for dt than grp 2.
I have created a simple Matrix based on this:
Since you are using SQL Server 2012, you can use the LookupSet function to get the First/Last values per row group.
The expression in the First row group TextBox is:
=Code.SumLookup(
LookupSet(
First(Fields!dt.Value, "grp").ToString & Fields!grp.Value.ToString
, Fields!dt.Value.ToString & Fields!grp.Value.ToString
, Fields!val.Value
, "DataSet1"
)
)
Based on my sample data, this is giving my required results:
Note that the second grp row has a narrower range than the first, but its first/last columns are independent for each group so are correct within each grp.
There are quite a few things going on here.
Custom code for aggregation of LookUpSet result
The LookupSet expression is wrapped in a Code.SumLookup custom function:
Function SumLookup(ByVal items As Object()) As Decimal
If items Is Nothing Then
Return Nothing
End If
Dim suma As Decimal = New Decimal()
suma = 0
For Each item As Object In items
suma += Convert.ToDecimal(item)
Next
Return suma
End Function
This is taken from the answer at this SO question.
This assumes that each matrix cell can be the sum of multiple values, so this needs to be summed up. LookupSet returns an array of values, which is aggregated by Code.SumLookup.
Details for LookupSet
Next, the LoopupSet expression itself:
LookupSet(
First(Fields!dt.Value, "grp").ToString & Fields!grp.Value.ToString
, Fields!dt.Value.ToString & Fields!grp.Value.ToString
, Fields!val.Value
, "DataSet1"
)
LookupSet takes the following parameters:
LookupSet(source_expression, destination_expression, result_expression, dataset)
In our expression, we want get all values from DataSet1 that match the first dt in the current grp scope.
For source_expression I use:
First(Fields!dt.Value, "grp").ToString & Fields!grp.Value.ToString
This gets the first dt in the row scope ("grp" is the name of the row group), then appends this to the current grp. This creates an expression to match to a similar expression when seeking in DataSet1.
i.e. destination_expression:
Fields!dt.Value.ToString & Fields!grp.Value.ToString
Finally, we specify we want Fields!val.Value as the result_expression and DataSet1 as the dataset parameter.
All matching Fields!val.Value values in DataSet1 are constructed into an array by LookupSet, then aggregated by Code.SumLookup.
Update expression for Last values
The expression for the Last TextBox is practically the same; just change First to Last:
=Code.SumLookup(
LookupSet(
Last(Fields!dt.Value, "grp").ToString & Fields!grp.Value.ToString
, Fields!dt.Value.ToString & Fields!grp.Value.ToString
, Fields!val.Value
, "DataSet1"
)
)
Get the Difference
Finally, to get the difference of these, simply subtract one expression from the other in the Difference TextBox, or even reference the ReportItems values:
=ReportItems!Last.Value - ReportItems!First.Value
Where Last and First are the names of the TextBoxes.
Conclusion
Obviously you will need to update for your specific case, but you can see that this can be done.
Is it worth doing this in your report? You can see there are many steps involved, and in general would be easier to address when generating the DataSet. But, if that is not an option hopefully this LookupSet approach is useful.
AFAIK this is not possible in SSRS alone. Believe me I've tried. Fortunately you have a SQL datasource so I would resolve this requirement there where you have (almost) unlimited powers to shape and manipulate data.
For example I would replace your final select with:
; WITH CTE_Base AS (
SELECT * FROM #tmpHoldingTable
)
, CTE_Test1 AS (
SELECT Test1
, ROW_NUMBER () OVER ( ORDER BY Test1 ) AS Column_Number_Test1
FROM CTE_Base
GROUP BY Test1
)
SELECT CTE_Base.*
, CTE_Test1.Column_Number_Test1
, CASE WHEN CTE_Test1.Column_Number_Test1 = 1
THEN Total
WHEN CTE_Test1.Column_Number_Test1 =
( SELECT MAX ( Column_Number_Test1 ) FROM CTE_Test1 )
THEN 0 - Total
ELSE 0
END AS [Difference]
FROM CTE_Base
INNER JOIN CTE_Test1
ON CTE_Base.Test1 = CTE_Test1.Test1
This adds a [Difference] column with a copy of [Total] for the 1st column and 0 - [Total] for the last column.
The SQL could probably be made more efficient, but hopefully breaking it into CTEs is easier to follow.
Then in the SSRS Designer you can add a [Difference] column outside the [Test1] column group and let it sum (default).
BTW your test data seems a bit simplistic - it will only produce 2 columns and all cells have values. But its great you posted DDL & DML - it made it easy to extend the data and code and test this.

Resources