Oracle: how to group by 1st value in row? - sql

Table has 3 columns in a table: ITEM, SUB_ITEM, DATE_CREATED.
ITEM - item id (string)
SUB_ITEM - item id (int)
DATE_CREATE - date the SUB_ITEM was created (date)
Scenario:
There are 3 Different item's (AAA1, AAB2, ABB3)
All 3 of these item's have multiple sub-items.
Each item has a sub-item that is the same for each of them (eg. All 3 of the item's have a SUB_ITEM = 101010)
I am trying to do something like:
select *
from table
group by ITEM, SUB_ITEM, DATE_CREATED
How do you make it display only 1 row? I don't care if it chooses AAA1 or AAB2 or ABB3, I just want it to pick 1 and remove the rest so it will show 1 row per SUB_ITEM, but still displays at least one of the parent items.
Edit:
Thank you to mathguy for answering the above question.
Question 2:
Is it possible to group by the 1st 2 letters of the item in addition to the sub_item? So instead of returning 1 row, return 2 rows: AAA1 and AAB2 will cascade in to 1 row, and ABB3 will be the 2nd row because 'AA' and 'AB' are different.
Edit 2: See Main Answer comments for answer to question 2

One way is to group by sub_item, and take the max or min over another column (let's say max over date_created) and whatever is in the remaining column IN THE SAME ROW.
select min(item) keep (dense_rank last order by date_created) as item,
sub_item, max(date_created) as date_created
from table_name
group by sub_item
;

Related

Select values from one column and display in two columns

I'm stumped trying to figure out a way to display data from one column in two different columns. The data is a bill of materials in Oracle, and I want to display certain items in the bill of materials in a second column.
For example, I have a master item which is 123ABC. Within this item it has the following items ... AA1, BB1, CC1. They all belong to the same column in the same table.
I'm trying to get a query to to display two columns, the first column would be master item, and the second column would be sub item that shows all items beginning with 'AA' like below...
Master Item | Sub Item
-------------------------
123ABC | AA1
So far I have the following...
select distinct msi.item FNUMBER
, msi.description DESCRIPTION
, (select msi1.item
FROM inv.mtl_sys_item msi1
WHERE msi1.item = msi.item)
from
, .mtl_sys_item msi
where msi.segment1 = '123ABC'
order by 1 desc
But this just displays the master item 'ABC123' in both columns instead of the AA1 in the second column.
Your data must have some sort of parent item column. You can use this to do a self-join:
SELECT msi.item as FNUMBER,
msi.description as DESCRIPTION,
msic.item
FROM inv.mtl_sys_item msi JOIN
inv.mtl_sys_item msic
ON msi.item = msic.parent_item -- guessing at the column name
WHERE msi.segment1 = '123ABC'
ORDER BY 1 desc

SQL - Possible to sum rows between particular values?

my apologies if this is a duplicate but I could not find an answer to my particular question. I have a table that lists products on a sales order, and their various quantities. Some products are components for other products and are denoted so with a flag. I would like to know if there is a way to have a running total for the parent/normal items that would reset on each parent/normal item.
Here is an example of the table data and my desired output:
OrderNo Item Qty Regular Line
349443 AFU20451-KIT1 1 Y 1
349443 AFU20451 0 N 2
349443 HAWKE-14252 1 N 3
349443 RGPM-25H4 1 N 4
349443 AV-003-265 1 Y 5
349443 AV-A00090-KIT 1 Y 6
349443 AV-A00091 1 N 7
349443 AV-A00090 1 N 8
349443 AV-00043 1 N 9
349443 AV457/310GR/FP 2 Y 10
desired output:
OrderNo Item Qty
349433 AFU20451-KIT1 3
349433 AV-003-265 1
349433 AV-A00090-KIT 4
349433 AV457/310GR/FP 2
As you can see, I would like to reset the sum every time it says Y, only include the parent item (I could get around this as I can keep the order of the items the same, could maybe use row number). I have been trying to use Over and Partition by in order to do this, but to no avail. Let me know if this is even possible or if you need any further information.
with cte as
(
select OrderNo,
-- only return the main item
case when Regular = 'Y' then Item end AS Item,
Qty,
-- assign a unique number to each `YNNN..` component group
-- needed for GROUP BY in next step
sum(case when Regular = 'Y' then 1 else 0 end)
over (partition by OrderNo
order by Line
rows unbounded preceding) as grp
from myTable
)
select OrderNo,
-- find the matching value for the main component
max(Item),
sum(Qty)
from cte
group by OrderNo, grp
Current representation is against 1st Codd's rule.
Rule 1: The information rule: All information in a relational data
base is represented explicitly at the logical level and in exactly one
way – by values in tables.
But I believe you can still create FUNCTION/PROCEDURE and iterate row one by one with IF statement for Y/N. E.g. you create new table, IF Y - add new row to table, IF N - add +1 to QTY to latest row.
I would create two separate tables: manufacturer & part, to get the values so you don't have to hand-jam each inventory, or care about where they fall in the invoice list.
[1
[]2
Then, all you would need to do is compare the values to the part table to get this data. It's more work upfront, but will pay off to have this all saved and stored. A future sample query would look something like:
SELECT OrderNo.OrderTable, Item.OrderTable, Sum(Qty.OrderTable) AS Quantity
FROM OrderTable INNER JOIN Part ON OrderTable.Item = Table.PartName
GROUP BY OrderNo.OrderTable, Item.OrderTable, Regular.OrderTable, Part.ParentID;
try this:
select orderno, item, sum(qty) over(partition by regular order by regular)
from your_table
group by orderno, item, regular

capture non occured data from dynamic input data

I have data rules like given below
1|Group1|Mandatory|1st occurrence
2|Group1|Optional|1st occurrence
3|Group1|Mandatory|1st occurrence
1|Group1|Mandatory|2nd occurrence
2|Group1|Optional|2nd occurrence
3|Group1|Mandatory|2nd occurrence
4|Group2|Mandatory|1st occurrence
5|Group2|Mandatory|1st occurrence
6|Group2|Optional|1st occurrence
Here as you can see Group 1 is present two times for data record 1, 2 and 3. It means group 1 can appear min 1 time and max two times. And also can see the occurrence of that specific record under group 1 when it occurs. Mandatory should occur always and optional is may or may not be occur in input data. But all needs to be captured ..what's missing
And here is my input column data. That's a only column am having in input data
1
2
3
1
2
4
5
Is there any way I could get result to identify which data set if missing according to data rules table from input data ? Like in this example, output should like saying Mandatory record(3) is missing from Group 1 in second occurrence. That's only available information would be coming from input data and data rules table.
If any things needs to be added to get desired result...I would like to hear..what it is. All suggestions are welcome.
Thanks
I think You need something like this:
with input as (select column_value id,
count(1) over (partition by column_value order by null
rows between unbounded preceding and current row) cnt
from table(sys.odcinumberlist(1, 2, 3, 1, 2, 4, 5)))
select *
from data
where status = 'Mandatory'
and (id, occurence) not in (select id, cnt from input)
demo
ID GRP STATUS OCCURENCE
---- ---------- ---------- ---------
3 Group1 Mandatory 2
Count how many times id appears in input data and compare result with mandatory occurences in your data.
Edit: explanation
select column_value id,
count(1) over (partition by column_value order by null
rows between unbounded preceding and current row) cnt
from table(sys.odcinumberlist(1, 2, 3, 1, 2, 4, 5))
This part simulates you input data. table(sys.odcinumberlist(1, 2, 3, 1, 2, 4, 5)) is just simulation of inputs, probably these ids are in some table, select them from there. For each provided id I'm counting it's growing number of occurences using function count() in analytic version, so we have this:
id cnt
--- ---
1 1
1 2
2 1
2 2
3 1
4 1
5 1
Next these pairs are compared with mandatory pairs (id, occurence) in your data. If something is missing last select displays this row with a clause not in.
This is how I understood Your question, perhaps You'll need some modifications, but now You have some hints. Hope this helps (and sorry for my bad English ;-) ).

Add Ordinal/Consecutive Value for similar values in table

OK - I have a table with the following columns - what I need to do, is in instances where there is a same visit_no, I need to populate a int field with an ordinal number (priority) for each code - e.g. - so the "Priority" field is what I am looking to have populated - a new successive number for each "Code" value within the each similar "visit_no"
Visit_no Code Priority
123456 97110 1
445566 85025 1
445566 71402 2
445566 71020 3
789888 80053 1
789888 97110 2
111111 85025 1
Dense_Rank is a function which increments the rank only when the value your ordering on changes.
http://msdn.microsoft.com/en-ca/library/ms173825.aspx
Select Visit_no, Code, Priority = DENSE_RANK() OVER (Partition by Visit_No order by Code)
From YourTable

Determine records which held particular "state" on a given date

I have a state machine architecture, where a record will have many state transitions, the one with the greatest sort_key column being the current state. My problem is to determine which records held a particular state (or states) for a given date.
Example data:
items table
id
1
item_transitions table
id item_id created_at to_state sort_key
1 1 05/10 "state_a" 1
2 1 05/12 "state_b" 2
3 1 05/15 "state_a" 3
4 1 05/16 "state_b" 4
Problem:
Determine all records from items table which held state "state_a" on date 05/15. This should obviously return the item in the example data, but if you query with date "05/16", it should not.
I presume I'll be using a LEFT OUTER JOIN to join the items_transitions table to itself and narrow down the possibilities until I have something to query on that will give me the items that I need. Perhaps I am overlooking something much simpler.
Your question rephrased means "give me all items which have been changed to state_a on 05/15 or before and have not changed to another state afterwards. Please note that for the example it added 2001 as year to get a valid date. If your "created_at" column is not a datetime i strongly suggest to change it.
So first you can retrieve the last sort_key for all items before the threshold date:
SELECT item_id,max(sort_key) last_change_sort_key
FROM item_transistions it
WHERE created_at<='05/15/2001'
GROUP BY item_id
Next step is to join this result back to the item_transitions table to see to which state the item was switched at this specific sort_key:
SELECT *
FROM item_transistions it
JOIN (SELECT item_id,max(sort_key) last_change_sort_key
FROM item_transistions it
WHERE created_at<='05/15/2001'
GROUP BY item_id) tmp ON it.item_id=tmp.item_id AND it.sort_key=tmp.last_change_sort_key
Finally you only want those who switched to 'state_a' so just add a condition:
SELECT DISTINCT it.item_id
FROM item_transistions it
JOIN (SELECT item_id,max(sort_key) last_change_sort_key
FROM item_transistions it
WHERE created_at<='05/15/2001'
GROUP BY item_id) tmp ON it.item_id=tmp.item_id AND it.sort_key=tmp.last_change_sort_key
WHERE it.to_state='state_a'
You did not mention which DBMS you use but i think this query should work with the most common ones.