Id Field_id Field_value
------------------------------
1 10 'A'
1 11 'B'
1 12 'C'
I want to make rows like
Id Field_id Field_value data_1 data_2
--------------------------------------
1 10 'A' 'B' 'C'
Pl help.
Take a look at this:
with t (Id, Field_id, Field_value) as (
select 1, 10, 'A' from dual union all
select 1, 11, 'B' from dual union all
select 1, 12, 'C' from dual
)
select FIELD_ID1, "10_FIELD_ID", "11_FIELD_ID","12_FIELD_ID"
from (select id, field_id, min(field_id) over() field_id1, field_value from t)
pivot (
max(field_value) field_id
for field_id in (10, 11, 12)
)
FIELD_ID1 10_FIELD_ID 11_FIELD_ID 12_FIELD_ID
---------------------------------------------------
10 A B C
Read more about pivot here
Normally, people look for PIVOT query, however, your input data is not at all duplicate as already mentioned in comments.
What you could do is, using subquery factoring, select id, 10 as field_id, field_value, thus making field_it static value as 10. Then use LISTAGG. But, the grouped rows would be a single column as delimeter seperated values.
I hope your requirement is exactly what you stated and not a typo or a mistake while posting.
Related
Is it possible to keep order from a 'IN' conditional clause?
I found this question on SO but in his example the OP have already a sorted 'IN' clause.
My case is different, 'IN' clause is in random order
Something like this :
SELECT SomeField,OtherField
FROM TestResult
WHERE TestResult.SomeField IN (45,2,445,12,789)
I would like to retrieve results in (45,2,445,12,789) order. I'm using an Oracle database. Maybe there is an attribute in SQL I can use with the conditional clause to specify to keep order of the clause.
There will be no reliable ordering unless you use an ORDER BY clause ..
SELECT SomeField,OtherField
FROM TestResult
WHERE TestResult.SomeField IN (45,2,445,12,789)
order by case TestResult.SomeField
when 45 then 1
when 2 then 2
when 445 then 3
...
end
You could split the query into 5 queries union all'd together though ...
SELECT SomeField,OtherField
FROM TestResult
WHERE TestResult.SomeField = 4
union all
SELECT SomeField,OtherField
FROM TestResult
WHERE TestResult.SomeField = 2
union all
...
I'd trust the former method more, and it would probably perform much better.
Decode function comes handy in this case instead of case expressions:
SELECT SomeField,OtherField
FROM TestResult
WHERE TestResult.SomeField IN (45,2,445,12,789)
ORDER BY DECODE(SomeField, 45,1, 2,2, 445,3, 12,4, 789,5)
Note that value,position pairs (e.g. 445,3) are kept together for readability reasons.
Try this:
SELECT T.SomeField,T.OtherField
FROM TestResult T
JOIN
(
SELECT 1 as Id, 45 as Val FROM dual UNION ALL
SELECT 2, 2 FROM dual UNION ALL
SELECT 3, 445 FROM dual UNION ALL
SELECT 4, 12 FROM dual UNION ALL
SELECT 5, 789 FROM dual
) I
ON T.SomeField = I.Val
ORDER BY I.Id
There is an alternative that uses string functions:
with const as (select ',45,2,445,12,789,' as vals)
select tr.*
from TestResult tr cross join const
where instr(const.vals, ','||cast(tr.somefield as varchar(255))||',') > 0
order by instr(const.vals, ','||cast(tr.somefield as varchar(255))||',')
I offer this because you might find it easier to maintain a string of values rather than an intermediate table.
I was able to do this in my application using (using SQL Server 2016)
select ItemID, iName
from Items
where ItemID in (13,11,12,1)
order by CHARINDEX(' ' + Convert("varchar",ItemID) + ' ',' 13 , 11 , 12 , 1 ')
I used a code-side regex to replace \b (word boundary) with a space. Something like...
var mylist = "13,11,12,1";
var spacedlist = replace(mylist,/\b/," ");
Importantly, because I can in my scenario, I cache the result until the next time the related items are updated, so that the query is only run at item creation/modification, rather than with each item viewing, helping to minimize any performance hit.
Pass the values in via a collection (SYS.ODCINUMBERLIST is an example of a built-in collection) and then order the rows by the collection's order:
SELECT t.SomeField,
t.OtherField
FROM TestResult t
INNER JOIN (
SELECT ROWNUM AS rn,
COLUMN_VALUE AS value
FROM TABLE(SYS.ODCINUMBERLIST(45,2,445,12,789))
) i
ON t.somefield = i.value
ORDER BY rn
Then, for the sample data:
CREATE TABLE TestResult ( somefield, otherfield ) AS
SELECT 2, 'A' FROM DUAL UNION ALL
SELECT 5, 'B' FROM DUAL UNION ALL
SELECT 12, 'C' FROM DUAL UNION ALL
SELECT 37, 'D' FROM DUAL UNION ALL
SELECT 45, 'E' FROM DUAL UNION ALL
SELECT 100, 'F' FROM DUAL UNION ALL
SELECT 445, 'G' FROM DUAL UNION ALL
SELECT 789, 'H' FROM DUAL UNION ALL
SELECT 999, 'I' FROM DUAL;
The output is:
SOMEFIELD
OTHERFIELD
45
E
2
A
445
G
12
C
789
H
fiddle
I have 2 tables and I need to do a table compare:
TABLE A
LABEL
VALUE
TABLE B
LABEL
VALUE
Basically I want:
Records in where the values are not equal on matching labels
Records in TABLE A that are not in TABLE B
Records in TABLE B that are not in TABLE A
With that information, I can record the proper historical data I need to. It will show me where the value has changed, or where a label was added or deleted......you can say TABLE A is the "new" set of data, and TABLE B is the "old" set of data. So I can see what is being added, what was deleted, and what was changed.
Been trying with UNION & MINUS, but no luck yet.
Something like:
A LABEL A VALUE B LABEL B VALUE
---------------------------------------
XXX 5 XXX 3
YYY 2
ZZZ 4
WWW 7 WWW 8
If the labels and values are the same, I do not need them in the result set.
Here is one way (and possibly the most efficient way) to solve this problem. The main part is the subquery that does a UNION ALL and GROUP BY on the result, keeping only groups consisting of a single row. (The groups with two rows are those where the same row exists in both tables.) This method was invented by Marco Stefanetti - first discussed on the AskTom discussion board. The benefit of this approach - over the more common "symmetric difference" approach - is that each base table is read just once, not twice.
Then, to put the result in the desired format, I use a PIVOT operation (available since Oracle 11.1); in earlier versions of Oracle, the same can be done with a standard aggregate outer query.
Note that I modified the inputs to show the handling of NULL in the VALUE column also.
Important: This solution assumes LABEL is primary key in both tables; if not, it's not clear how the required output would even make sense.
with
table_a ( label, value ) as (
select 'AAA', 3 from dual
union all select 'CCC', null from dual
union all select 'XXX', 5 from dual
union all select 'WWW', 7 from dual
union all select 'YYY', 2 from dual
union all select 'HHH', null from dual
),
table_b ( label, value ) as (
select 'ZZZ', 4 from dual
union all select 'AAA', 3 from dual
union all select 'HHH', null from dual
union all select 'WWW', 8 from dual
union all select 'XXX', 3 from dual
union all select 'CCC', 1 from dual
)
-- End of test data (NOT PART OF THE SOLUTION!) SQL query begins below this line.
select a_label, a_value, b_label, b_value
from (
select max(source) as source, label as lbl, label, value
from (
select 'A' as source, label, value
from table_a
union all
select 'B' as source, label, value
from table_b
)
group by label, value
having count(*) = 1
)
pivot ( max(label) as label, max(value) as value for source in ('A' as a, 'B' as b) )
;
Output:
A_LABEL A_VALUE B_LABEL B_VALUE
------- ------- ------- -------
YYY 2
CCC CCC 1
WWW 7 WWW 8
ZZZ 4
XXX 5 XXX 3
i need a help to get solution to my problem, Please.
I have a table like this :
ID Number
|6 |20.90 |
|7 |45.00 |
|8 |52.00 |
|9 |68.00 |
|10 |120.00 |
|11 |220.00 |
|12 |250.00 |
The first range is 0 - 20.90.
When the value is in the half, the value id is for the max range.
When i got value 20.91, i want to get "ID = 6".
If the value is 31.00, i want to get "ID = 6"
If the value is
33.95, i want to get "ID = 7".
if the value is 44.99, i want to get ID = 7
How i can do it? Is there a function that will do what I need?
If you want the record with a number that is closest to your input, then you can use this:
select *
from (
select *
from mytable
order by abs(number - my_input_number), id
)
where rownum < 2
The inner query selects all records, but orders them by the distance they have from your input number. This distance can be calculated with number - my_input_number. But that could be negative, so we take the absolute value of that. This result is not output; it is just used to order by. So records with smaller distances will come first.
Now we need just the first of those records, and that is what the outer query does with the typical Oracle reserved word rownum: it represents a sequence number for every record of the final result set (1, 2, 3, ...). The where clause will effectively filter away all records we do not want to see, leaving only one (with smallest distance).
As mathguy suggested in comments, the order by now also has a second value to order by in case the input value is right at the mid point between the two closest records. In that case the record with the lowest id value will be chosen.
This is a good illustration of the power of analytic functions:
with mytable ( id, value ) as (
select 6, 20.90 from dual union all
select 7, 45.00 from dual union all
select 8, 52.00 from dual union all
select 9, 68.00 from dual union all
select 10, 120.00 from dual union all
select 11, 220.00 from dual union all
select 12, 250.00 from dual
),
inputs ( x ) as (
select 0.00 from dual union all
select 20.91 from dual union all
select 31.00 from dual union all
select 33.95 from dual union all
select 44.99 from dual union all
select 68.00 from dual union all
select 32.95 from dual union all
select 400.11 from dual
)
-- End of test data (not part of the solution). SQL query begins BELOW THIS LINE
select val as x, new_id as closest_id
from (
select id, val,
last_value(id ignore nulls) over (order by val desc) as new_id
from (
select id, (value + lead(value) over (order by value))/2 as val
from mytable
union all
select null, x
from inputs
)
)
where id is null
order by x -- if needed
;
Output:
X CLOSEST_ID
------ ----------
0 6
20.91 6
31 6
32.95 6
33.95 7
44.99 7
68 9
400.11 12
I am trying to create a distinct list of parts to do analysis on in a table. The table contains a column of Part IDs and a column of Identifiers. The Identifiers are separated within the same entry by pipes but unfortunately the Identifiers are out of order. I'm not sure if this is possible but any help would be greatly appreciated!
For example (currently Both ID and Identifiers are VARCHAR2)
ID Identifiers
1 |1|2|
1 |2|1|
2 |3|A|1|B|
2 |B|1|3|A|
3 |1|3|2|
3 |1|5|
3 |2|1|3|
4 |AA|BB|1|3A|
4 |1|3A|AA|BB|
and I need the query to return
ID Identifiers
1 |1|2|
2 |3|A|1|B|
3 |1|5|
3 |1|3|2|
4 |1|AA|BB|3A|
It does not matter what specific order the identifiers are ordered in as long as all contents within that identifier are the same. For example, |1|5| or |5|1| doesn't matter but I need to see both entries |1|5| and |1|3|2. My original thought was to create separate out the identifiers into separate columns and then alphabetically concatenate back into one column but i'm not sure...thanks in advance!
Something like this (assuming there are no duplicate rows in the input table - if there are, the solution needs to be modified a bit).
In the solution I build the test_table for testing (it is not part of the solution), and I build another factored subquery in the WITH clause. This works in Oracle 11 and above. For earlier versions of Oracle, the subquery defined as prep needs to be moved as a subquery within the final query instead.
with
test_table ( id, identifiers ) as (
select '1', '|1|2|' from dual union all
select '1', '|2|1|' from dual union all
select '2', '|3|A|1|B|' from dual union all
select '2', '|B|1|3|A|' from dual union all
select '3', '|1|3|2|' from dual union all
select '3', '|1|5|' from dual union all
select '3', '|2|1|3|' from dual union all
select '4', '|AA|BB|1|3A|' from dual union all
select '4', '|1|3A|AA|BB|' from dual
),
prep ( id, identifiers, token ) as (
select id, identifiers, regexp_substr(identifiers, '[^|]+', 1, level)
from test_table
connect by level <= regexp_count(identifiers, '\|') - 1
and prior identifiers = identifiers
and prior sys_guid() is not null
)
select distinct id,
'|' || listagg(token, '|') within group (order by token) || '|'
as identifiers
from prep
group by id, identifiers
order by id, identifiers -- ORDER BY is optional
;
Output:
ID IDENTIFIERS
--- --------------------
1 |1|2|
2 |1|3|A|B|
3 |1|2|3|
3 |1|5|
4 |1|3A|AA|BB|
5 rows selected.
sample data: table consists of 96000 rows and i want to combine them into 16000 rows
row 1:1255467861 40825825 IDF+0149502016010615210300000396000026+0000651+ 00000000000000+|
row 2:000000000+0000000+0000000+000000677+02 YY 0444410100000 00 0001000000054+10001EB4200002+00000+0000052+0000000+0000000+|
row 3:00000 00000+00000+0000000+0000000+0000000+00000 00000+00000+0000000+0000000+0000000+00000 00000+00000+0000000+0000000+|
row 4:0000000+00000 00000+00000+0000000+0000000+0000000+00 004 1 000000000000 0000000000 M5|
row 5: 00000000 +00000000000000000000000000000000000000+00000000001011 Y 000000000+|
row 6:0000000+0000000+0000000+AB0002210000000000FIABMM81 15067195 0000000000009403228870|
Assuming that you know what the criteria (column1, column2 in the example) for grouping your rows is, I suggest you to use aggregate functions functions such as SUM(), MAX(), and COUNT() to aggregate data, Group by the criteria, and insert into the target table.
E.G.
insert into my_target_table
Select column1, column2, sum(column3), max(column4), count(1) as column5
from my_source_table
group by column_1, column2
Assuming "row 1, row 2, row 3" has some meaning, you must have another column somewhere that shows the order. In my setup below, I assume you have an "id" (not necessarily consecutive numbers starting from 1; it can be number, or it can be date, etc. - for illustration I use some random numbers, they can also be fractional, or negative etc. - I build "row numbers" form that). If you don't have ANY kind of ordering, in the "order by" clause for the row_number() function you can simply "order by 1". However, I assume you also want to capture the minimum value of the "id" from each group of six (again, the id may be a date or timestamp or whatever).
The strategy is to create "row numbers". It works better if everything starts from 0, so I subtract 1 from the row_number() values. Then group by trunc(rn/6) and order by mod(rn, 6).
To concatenate the strings use listagg. I assume you wanted the strings concatenated with NO separator between them; if you do want them separated, for example, by #, then change the second argument to listagg from '' to '#'.
Query (including setup data, I shortened the input strings and I added a few more to show how everything works):
with test_data(id, input_str) as (
select 1, '12554861 40825825 IDF+0140000000+|' from dual union all
select 2, '0000000+00052+0000000+0000000+|' from dual union all
select 4, '00000 000+00000+0+00000+000+0000000+|' from dual union all
select 9, '00000+0000000+000000000000 M5|' from dual union all
select 14, '000000 +000000000011 Y 000000000+|' from dual union all
select 23, '000000IABMM81 15067195 0000003228870|' from dual union all
select 31, '125508825 IDF+0143333300000+|' from dual union all
select 32, '0000+052+0000AXZC000+0000000+|' from dual union all
select 37, '000MMDOQ000+0+00000+0000000+0000000+|' from dual union all
select 45, '000000 +00000000001011 Y 000000000+|' from dual union all
select 46, '00000+0000FIAB1 15067195 000CCV70|' from dual
),
has_rn (id, input_str, rn) as (
select id, input_str, row_number() over (order by id) - 1
from test_data
)
select min(id) as id,
listagg(input_str, '') within group (order by mod(rn, 6)) as output_str
from has_rn
group by trunc(rn/6);
Output (using the sample inputs in the test_data cte):
ID OUTPUT_STR
-- -------------------------------------------------------------------------------------------------------------------
1 12554861 40825825 IDF+0140000000+|0000000+00052+0000000+0000000+|00000 000+00000+0+00000+000+0000000+|00000+0000000+000000000000 M5|000000 +000000000011 Y 000000000+|000000IABMM81 15067195 0000003228870|
31 125508825 IDF+0143333300000+|0000+052+0000AXZC000+0000000+|000MMDOQ000+0+00000+0000000+0000000+|000000 +00000000001011 Y 000000000+|00000+0000FIAB1 15067195 000CCV70|