Sorry if Im not clear in title, I have some language problems. I have a table (quite big) like
Person value Letter
------------------------------------------
Tom value1 A
Tom value2 T
Ann value1 F
Ann value2 R
Ann value3 Y
Jim value3 W
I would like to shorten it into:
Person value1 value2 value3
------------------------------------------
Tom A T (null)
Ann F R Y
Jim (null) (null) W
Something like listagg, but into different columns. Looks simple but I'm stuck...
edit: There are 8 values, not just 3, I wanted made it simplier, values don't repeat for the same person (but can be null/not appear))
Here's one option: the idea is to use an aggregate function (such as MIN, MAX, SUM). As your sample data contain only 3 values, that's what I did as well - you'd use 5 more lines like this (including them from line #13 onward).
Note that lines #1 - 8 represent your sample data; you already have them stored in the table so you wouldn't be typing that. Code you actually need begins at line #9.
SQL> with test (person, value, letter) as
2 (select 'Tom', 'val1', 'A' from dual union all
3 select 'Tom', 'val2', 'T' from dual union all
4 select 'Ann', 'val1', 'F' from dual union all
5 select 'Ann', 'val2', 'R' from dual union all
6 select 'Ann', 'val3', 'Y' from dual union all
7 select 'Jim', 'val3', 'W' from dual
8 )
9 select
10 person,
11 max(decode(value, 'val1', letter)) value1,
12 max(decode(value, 'val2', letter)) value2,
13 max(decode(value, 'val3', letter)) value3
14 from test
15 group by person
16 order by person;
PERSON VALUE1 VALUE2 VALUE3
------ ------ ------ ------
Ann F R Y
Jim W
Tom A T
SQL>
I have a table where values in certain columns can change. My requirement is to identify the items(id) where the value has changed. E.g.
Input:
ID VALUE1 VALUE2 VALUE3
1 A B C
1 X B C
2 D E F
2 D E F
3 G H I
3 S H T
Output Needed:
ID VALUE1 VALUE2 VALUE3
1 X
3 S T
I am using Oracle SQL. ANy help will be appreciated
This could be a way, assuming that you need to order your records by some column (I added a row_num column just to explain):
select *
from (
select ID,
case when lag(value1) over (partition by ID order by row_num) != value1 then value1 end as value1,
case when lag(value2) over (partition by ID order by row_num) != value2 then value2 end as value2,
case when lag(value3) over (partition by ID order by row_num) != value3 then value3 end as value3
from yourTable
)
where value1 is not null
or value2 is not null
or value3 is not null
This uses lag to get the value in the preceding row (ordered by row_num) for the same ID and then simply checks if there's at least one difference.
With your sample data, this
with yourTable(row_num, ID, VALUE1, VALUE2, VALUE3) as (
select 1, 1, 'A', 'B', 'C' from dual union all
select 2, 1, 'X', 'B', 'C' from dual union all
select 3, 2, 'D', 'E', 'F' from dual union all
select 4, 2, 'D', 'E', 'F' from dual union all
select 5, 3, 'G', 'H', 'I' from dual union all
select 6, 3, 'S', 'H', 'T' from dual
)
select *
from (
select ID,
case when lag(value1) over (partition by ID order by row_num) != value1 then value1 end as value1,
case when lag(value2) over (partition by ID order by row_num) != value2 then value2 end as value2,
case when lag(value3) over (partition by ID order by row_num) != value3 then value3 end as value3
from yourTable
)
where value1 is not null
or value2 is not null
or value3 is not null
gives
ID VALUE1 VALUE2 VALUE3
---------- ------ ------ ------
1 X
3 S T
2 rows selected.
Your solution needs three parts.
You need to define an order for the changes. In your example it is "from top down to bottom". In your table, you'll have likely a date column or numerical id.
As #Aleksej suggests, you can then access the previous value with LAG(value) OVER (... ORDER BY ...)
You need a comparision function, which handles NULL values correctly. This is a bit painful, and there are more solutions to it, none of which is nice. I'd recommend DECODE(old_value, new_value, 0, 1)=1, see here for other examples.
I've added some extra rows to your table to test the changes involving NULL values:
CREATE TABLE mytable (id NUMBER, value1 VARCHAR2(1), value2 VARCHAR2(1), value3 VARCHAR2(1), t TIMESTAMP DEFAULT SYSTIMESTAMP);
INSERT INTO mytable (id,value1,value2,value3) VALUES (1, 'A','B','C');
INSERT INTO mytable (id,value1,value2,value3) VALUES (1, 'X','B','C');
INSERT INTO mytable (id,value1,value2,value3) VALUES (2, 'D','E','F');
INSERT INTO mytable (id,value1,value2,value3) VALUES (2, 'D','E','F');
INSERT INTO mytable (id,value1,value2,value3) VALUES (3, 'G','H','I');
INSERT INTO mytable (id,value1,value2,value3) VALUES (3, 'S','H','T');
INSERT INTO mytable (id,value1,value2,value3) VALUES (3, 'S','H',NULL);
INSERT INTO mytable (id,value1,value2,value3) VALUES (3, 'S','H',NULL);
INSERT INTO mytable (id,value1,value2,value3) VALUES (3, 'S','U','T');
SELECT ID,
CASE WHEN value1_changed=1 THEN value1 END AS value1,
CASE WHEN value2_changed=1 THEN value2 END AS value2,
CASE WHEN value3_changed=1 THEN value3 END AS value3,
value1_changed,
value2_changed,
value3_changed
FROM (
SELECT id, value1, value2, value3,
DECODE(value1, LAG(value1) OVER (PARTITION BY ID ORDER BY t), 0, 1) value1_changed,
DECODE(value2, LAG(value2) OVER (PARTITION BY ID ORDER BY t), 0, 1) value2_changed,
DECODE(value3, LAG(value3) OVER (PARTITION BY ID ORDER BY t), 0, 1) value3_changed,
row_number() OVER (PARTITION BY ID ORDER BY t) AS r, t
FROM mytable
)
WHERE r > 1
AND value1_changed + value2_changed + value3_changed >= 0;
ID value1 value2 value3 changed1 changed2 changed3
1 X 1 0 0
3 S T 1 0 1
3 0 0 1
3 U 0 0 1
Please not the 3rd line, when value3 changed from 'T' to NULL. It gets correctly reported, but only with it's new value NULL.
How can I traverse records (actually a subset of columns) into one-record columns - up to 99 columns -- for a huge table?
I mean, I have a table with following sample structure/data :
TABLE_ORI
COLUMN1 COLUMN2 COLUMN3 CODE VALUE
------- ------- ------- ---- ------------
C1 C2 C3 1 Value1
C1 C2 C3 2 Value2
C1 C2 C3 3 Value3
C100 C39 C21 1 Value40
C100 C39 C21 2 Value41
I want to convert this data into:
TABLE_NEW
COLUMN1 COLUMN2 COLUMN3 VALUE1 VALUE2 VALUE3 VALUE4 VALUE5 ... VALUE99
------- ------- ------- ------ ------ ------- ------ ------ -------
C1 C2 C3 Value1 Value2 Value3
C100 C39 C21 Value40 Value41
Please consider this is a big table and result table can have up to 99 value columns. I tried a PL/SQL with nested loop besides bulk collect cursor but the process takes days and never ends.
Thanks a lot!
This would probably be the fastest way:
create table table_new as
select /*+ parallel */ column1, column2, column3,
max(case when code = 1 then value else null end) value1,
max(case when code = 2 then value else null end) value2,
max(case when code = 3 then value else null end) value3,
max(case when code = 4 then value else null end) value4,
max(case when code = 5 then value else null end) value5,
--...
max(case when code = 99 then value else null end) value99
from table_ori
group by column1, column2, column3;
That assumes you have Enterprise Edition, a database that is configured to use parallelism properly, a large amount of tablespace to sort all the data at one time, etc.
It would also help performance to use the option NOLOGGING when creating the table. That would avoid generating a lot of REDO and UNDO although at the expense of the table not being recoverable.
For large processes like this, Real-Time SQL Monitoring is the perfect way to diagnose problems. If the above SQL is taking a long time, run a statement like this to monitor the SQL and see what operations and events are taking up the most time:
select dbms_sqltune.report_sql_monitor('$the_real_sql_id') from dual;
I have a data table with one column called "position". There can be no duplicates. I want to be able to reorder the rows if the value of this column is changed. For example,
**Name Column2 Position**
Name1 value1 1
Name2 Value2 2
Name3 Value3 3
Name4 Value4 4
Name5 Value5 5
If I change the position number 3 to 1, then the table would look like:
**Name Column2 Position**
Name3 Value3 1
Name1 value1 2
Name2 Value2 3
Name4 Value4 4
Name5 Value5 5
If I change the position number 3 to 5, in original data table, then:
**Name Column2 Position**
Name1 value1 1
Name2 Value2 2
Name4 Value4 3
Name5 Value5 4
Name3 Value3 5
(kind of like NetFlix, when you reorder your movie list)
The question is long ago but maybe this could help someone who has the same problem.
//Create a DataView from your table.
DataView view = new DataView(YourTable);
// Sort by Position column
view.Sort = "Position ASC";
//and get your new table
newTable = view.ToTable;
// or you just use the DataView for diplaying your values
I have a doubt in retrieving records in CRM 2011 using C# RetrieveMultiple method in a web service.
I need to retrieve distinct records from an entity based on a attribute value(primary key). I am able to achieve this by using the code below
QueryExpression query = new QueryExpression("entityname");
query.ColumnSet.AddColumns("primarycolumn", "column1");
query.Distinct = true;
EntityCollection result1 = serviceProxy.RetrieveMultiple(query);
Output:
primarycolumn column1
xyz 1
lmn 2
This displays distinct records. But if I add few more columns to the columnset, the results are not distinct. This is shown in the code below
QueryExpression query = new QueryExpression("entityname");
query.ColumnSet.AddColumns("primarycolumn", "column1", "column2");
query.Distinct = true;
EntityCollection result1 = serviceProxy.RetrieveMultiple(query);
Output:
primarycolumn column1 column2
xyz 1 a
xyz 1 b
lmn 2 a
I need the result to be distinct only based on the primary column.
Please help me in how to achieve this.
More information on the above question.
Actually, the code is in C#. The table below shows the table which I am querying
**Primary
column Column1 Column2 Column3<br/>**
Xyz Value1 Value1 Value1 <br/>
Xyz Value2 Value2 Value2<br/>
Lmn Value1 Value1 Value1<br/>
Lmn Value2 Value2 Value2<br/>
Xyz Value1 Value1 Value1<br/>
Lmn Value1 Value1 Value1<br/>
The query result should be like below. Where only the primary columns distinct values must be considered and all other columns can be either distinct or not distinct. Any one row of the distinct value of the primary column should only be displayed.
(This is the output I am trying to achieve)<br/>
**Prmrycolumn Column1 Column2 Column3<br/>**
Xyz Value1 Value1 Value1<br/>
Lmn Value1 Value1 Value1<br/>
The below code gives the following output
QueryExpression query = new QueryExpression("entityname");
query.ColumnSet.AddColumns("primarycolumn", "column1");
query.Distinct = true;
EntityCollection result1 = serviceProxy.RetrieveMultiple(query);
**Primary column Column1**<br/>
Xyz Value1<br/>
Lmn Value1<br/>
But as I add more columns to the columnset, the output is considering distinct values of other columns also
QueryExpression query = new QueryExpression("entityname");
query.ColumnSet.AddColumns("primarycolumn", "column1", "column2");
query.Distinct = true;
EntityCollection result1 = serviceProxy.RetrieveMultiple(query);
**Primary
column Column1 Column2 Column3<br/>**
Xyz Value1 Value1 Value1<br/>
Xyz Value2 Value2 Value2<br/>
Lmn Value1 Value1 Value1<br/>
Lmn Value2 Value2 Value2<br/>
ctually, the code is in C#. The table below shows the table which I am querying
**Primary
column Column1 Column2 Column3<br/>**
Xyz Value1 Value1 Value1 <br/>
Xyz Value2 Value2 Value2<br/>
Lmn Value1 Value1 Value1<br/>
Lmn Value2 Value2 Value2<br/>
Xyz Value1 Value1 Value1<br/>
Lmn Value1 Value1 Value1<br/>
The query result should be like below. Where only the primary columns distinct values must be considered and all other columns can be either distinct or not distinct. Any one row of the distinct value of the primary column should only be displayed.
(This is the output I am trying to achieve)<br/>
**Prmrycolumn Column1 Column2 Column3<br/>**
Xyz Value1 Value1 Value1<br/>
Lmn Value1 Value1 Value1<br/>
The below code gives the following output
QueryExpression query = new QueryExpression("entityname");
query.ColumnSet.AddColumns("primarycolumn", "column1");
query.Distinct = true;
EntityCollection result1 = serviceProxy.RetrieveMultiple(query);
**Primary column Column1**<br/>
Xyz Value1<br/>
Lmn Value1<br/>
But as I add more columns to the columnset, the output is considering distinct values of other columns also
QueryExpression query = new QueryExpression("entityname");
query.ColumnSet.AddColumns("primarycolumn", "column1", "column2");
query.Distinct = true;
EntityCollection result1 = serviceProxy.RetrieveMultiple(query);
**Primary
column Column1 Column2 Column3<br/>**
Xyz Value1 Value1 Value1<br/>
Xyz Value2 Value2 Value2<br/>
Lmn Value1 Value1 Value1<br/>
Lmn Value2 Value2 Value2<br/>
Editted Answer:
I need the result to be distinct only based on the primary column.
The above requirement is not possible using the RetrieveMultiple service call. What you are essentially asking for is a way to retrieve the first record for each record with a unique primary column. This might be possible using generated Early Bound entities but it is not possible using the standard service methods alone.
To achieve what you are after, you will need to retrieve all the records, which is what your current query does:
ueryExpression query = new QueryExpression("entityname");
query.ColumnSet.AddColumns("primarycolumn", "column1", "column2");
query.Distinct = true; // this distinct will apply over all columns
EntityCollection result1 = serviceProxy.RetrieveMultiple(query);
Once this data has been received, you will need to filter out all the records you want to ignore. In your case, you only want the first record found. You can achieve this using C# code as follows:
// this code filters out all records except the
// first for each unique primary column
var unique = result1.Entities.GroupBy(item => item.GetAttributeValue<Guid>("primarycolumn"))
.Select(item => item.First());