Cannot figure out logic to update a table - sql

I have below table.
TABLE: ABCD
B column have value 1 whenever there is a change in A column. Now I have to update the table like below. How can I do that?

You can do this using a correlated subquery:
update t
set b = (select sum(t2.b) from t t2 where t2.A <= t.A);
This is standard SQL and should work in either Oracle or Teradata.

Lets have a slightly more complicated example (where the changes in B are not correlated to the changes in A):
Oracle Setup:
CREATE TABLE ABCD( A, B ) AS
SELECT 1, 0 FROM DUAL UNION ALL
SELECT 1, 0 FROM DUAL UNION ALL
SELECT 1, 1 FROM DUAL UNION ALL
SELECT 2, 1 FROM DUAL UNION ALL
SELECT 2, 0 FROM DUAL UNION ALL
SELECT 3, 0 FROM DUAL UNION ALL
SELECT 3, 1 FROM DUAL UNION ALL
SELECT 3, 0 FROM DUAL;
Update:
UPDATE ABCD t1
SET b = (
SELECT sm
FROM (
SELECT ROWID AS id,
SUM( b ) OVER ( ORDER BY a, ROWNUM ) AS sm
FROM ABCD
) t2
WHERE t1.ROWID = t2.ID
);
Output:
SELECT * FROM ABCD;
A B
- -
1 0
1 0
1 1
2 2
2 2
3 2
3 3
3 3
(Note: This is an Oracle solution; I have no idea if it will or won't work in Teradata.)

Related

how to select set of records is ID is present in one of them

Here is the table where ORGID/USERID makes unique combination:
ORGID USERID
1 1
1 2
1 3
1 4
2 1
2 5
2 6
2 7
3 9
3 10
3 11
I need to select all records (organizations and users) wherever USERID 1 is present. So USERID=1 is present in ORGID 1 and 2 so then select all users for these organizations including user 1 itself, i.e.
ORGID USERID
1 1
1 2
1 3
1 4
2 1
2 5
2 6
2 7
Is it possible to do it with one SQL query rather than SELECT *.. WHERE USERID IN (SELECT...
You could use exists:
select *
from mytable t
where exists (select 1 from mytable t1 where t1.orgid = t.orgid and t1.userid = 1)
Another option is window functions. In Postgres:
select *
from (
select t.*,
bool_or(userid = 1) over(partition by orgid) has_user_1
from mytable t
) t
where has_user_1
Or a more generic approach, that uses portable expressions:
select *
from (
select t.*,
max(case when userid = 1 then 1 else 0 end) over(partition by orgid) has_user_1
from mytable t
) t
where has_user_1 = 1
Yes, you can do it with a single select statement - no in or exists conditions, no analytic or aggregate functions in a subquery, etc. Why you want to do it that way is not clear; in any case, it is possible that the solution below is also more efficient than the alternatives. You will have to test on your real-life data to see if that is true.
The solution below has two potential disadvantages: it only works in Oracle (it uses a proprietary extension of SQL, the match_recognize clause); and it only works in Oracle 12.1 or higher.
with
my_table(orgid, userid) as (
select 1, 1 from dual union all
select 1, 2 from dual union all
select 1, 3 from dual union all
select 1, 4 from dual union all
select 2, 1 from dual union all
select 2, 5 from dual union all
select 2, 6 from dual union all
select 2, 7 from dual union all
select 3, 9 from dual union all
select 3, 10 from dual union all
select 3, 11 from dual
)
-- End of SIMULATED data (for testing), not part of the solution.
-- In real life you don't need the WITH clause; reference your actual table.
select *
from my_table
match_recognize(
partition by orgid
all rows per match
pattern (x* a x*)
define a as userid = 1
);
Output:
ORGID USERID
---------- ----------
1 1
1 2
1 3
1 4
2 1
2 5
2 7
2 6
You can use exists:
select ou.*
from orguser ou
where exists (select 1
from orguser ou ou2
where ou2.orgid = ou.orgid and ou2.userid = 1
);
Apart from Exists and windows function, you can use IN as follows:
select *
from your_table t
where t.orgid in (select t1.orgid from your_table t1 where t1.userid = 1)

Query to delete duplicate records by keeping original in oracle

This is the table.
Id. Name
1 A
1 A
2 B
2 C
1 A
2 B
2 D
The output should be
Id. Name
1 A
2 B
2 C
2 D
please try
Select distinct id, name
from <name of you table>
order by name
Check this link.
Sample data:
create table demo (id, name) as
select 1, 'A' from dual union all
select 1, 'A' from dual union all
select 2, 'B' from dual union all
select 2, 'C' from dual union all
select 1, 'A' from dual union all
select 2, 'B' from dual union all
select 2, 'D' from dual;
select * from demo order by 1,2;
ID NAME
---------- ------------------------------
1 A
1 A
1 A
2 B
2 B
2 C
2 D
7 rows selected
Delete all but the first row in each (id, name) group:
delete demo where rowid in
( select lag(rowid) over (partition by id, name order by null) from demo );
3 rows deleted
select * from demo order by 1,2
ID N
---------- -
1 A
2 B
2 C
2 D
4 rows selected.

Apply a select for every element of a column

I would like to update third with a select that uses column first
|first #|second #|third #|
|_______|________|_______|
|___1___|___1____|_null__|
|___5___|___2____|_null__|
|___3___|___6____|_null__|
|___2___|___4____|_null__|
In pseudo code:
for row in table:
row.third = result_of_a_select(row.first)
What is the equivalent on SQL?
My wrong attempt:
update example_table
set third=
(
SELECT MAX(CDARTI) FROM
(
SELECT A.CDARTI
FROM PGMR.UT_ART_CODALT T,
PGMR.MRP_ARCH_ARTICOLI A
WHERE
A.CDARTI = T.CDARTI AND
T.CDARTI = first
UNION
SELECT A.CDARTI
FROM PGMR.UT_ART_CODALT T,
PGMR.MRP_ARCH_ARTICOLI A
WHERE
A.CDARTI = T.CDARTI_A AND
T.CDARTI = first
UNION
SELECT A.CDARTI
FROM PGMR.UT_ART_CODALT T,
PGMR.MRP_ARCH_ARTICOLI A
WHERE
A.CDARTI = T.CDARTI AND
T.CDARTI_A = first
UNION
SELECT A.CDARTI
FROM PGMR.UT_ART_CODALT T,
PGMR.MRP_ARCH_ARTICOLI A
WHERE
A.CDARTI = T.CDARTI_A AND
T.CDARTI = (SELECT T2.CDARTI FROM PGMR.UT_ART_CODALT T2 WHERE T2.CDARTI_A = first)
)
);
commit;
Works for me.
update t1 set third = (select third from t2 where t2.first = t1.first);
Fiddle (switched to MySql DB since I can't make Oracle work in it, but tested against my local Oracle as well).
The problem is not in the approach, but in your query. To fix that, you'll need to provide a Minimal, Complete, and Verifiable example.
A simplified test case:
SQL> create table yourTable (first, second, third) as (
2 select 1, 1, cast (null as number) from dual union all
3 select 5, 2, cast (null as number) from dual union all
4 select 3, 6, cast (null as number) from dual union all
5 select 2, 4, cast (null as number) from dual
6 );
Table created.
SQL> update yourTable t
2 set third = (select t.first * 2 from dual);
4 rows updated.
SQL> select * from yourTable;
FIRST SECOND THIRD
---------- ---------- ----------
1 1 2
5 2 10
3 6 6
2 4 4
SQL>
To make it a slightly more interesting/illustrative example I'm updating the third column with the value from a mapping table and have included duplicate values.
You can use MERGE and match on the pseudocolumn ROWID:
Oracle Setup:
CREATE TABLE table_name ( first, second, third ) AS
SELECT 1, 1, CAST( NULL AS NUMBER ) FROM DUAL UNION ALL
SELECT 5, 2, NULL FROM DUAL UNION ALL
SELECT 3, 6, NULL FROM DUAL UNION ALL
SELECT 2, 4, NULL FROM DUAL UNION ALL
SELECT 3, 4, NULL FROM DUAL;
CREATE TABLE table_name_map ( first, value ) AS
SELECT 1, 9 FROM DUAL UNION ALL
SELECT 2, 8 FROM DUAL UNION ALL
SELECT 3, 7 FROM DUAL UNION ALL
SELECT 4, 6 FROM DUAL UNION ALL
SELECT 5, 5 FROM DUAL;
Update:
MERGE INTO table_name dst
USING ( SELECT t.ROWID AS ri,
m.value
FROM table_name t
INNER JOIN table_name_map m
ON ( t.first = m.first )
) src
ON ( src.ri = dst.ROWID )
WHEN MATCHED THEN
UPDATE SET third = src.value;
Result:
FIRST SECOND THIRD
----- ------ -----
1 1 9
5 2 5
3 6 7
2 4 8
3 4 7

is not distinct from in join on clause oracle

I have a query structured with the left outer join like so:
left outer JOIN GA_LOAN GA
ON LOAN.LOAN_TYPE = GA.LOAN_TYP
AND LOAN.DT = GA.GUARANTY_DT
AND LOAN.FFEL_DUP_ID = GA.SEP_LOAN_IND
AND LOAN.SCH_BR_CODE = GA.ORIG_SCHL_CD
AND STU.CURR_SSN = GA.STU_SSN
AND STU.DOB = GA.DOB
and stu.curr_fst = ga.stu_first_nam
--and (plus_bor.curr_ssn is not distinct from ga.plus_brwr_ssn )
When I add the commented out line, I get the following error.
ORA-00908: missing NULL keyword
00908. 00000 - "missing NULL keyword"
*Cause:
*Action:
is not distinct from works fine in this structure in DB2, but Oracle is giving me issues. Any suggestions?
I get no errors if I replaced is not distinct from with a = but that isn't the same logically.
is not distinct from with give a match if both values are null, where as = would not match in this case.
The simplest way to emulate IS [ NOT ] DISTINCT FROM in Oracle is by using DECODE:
-- a IS DISTINCT FROM b
DECODE(a, b, 1, 0) = 0
-- a IS NOT DISTINCT FROM b
DECODE(a, b, 1, 0) = 1
This is what you're getting when you're using jOOQ's SQL dialect translator. A dbfiddle for this:
WITH t (x) AS (
SELECT 1 FROM dual UNION ALL
SELECT 2 FROM dual UNION ALL
SELECT null FROM dual
)
SELECT
t1.x AS x1,
t2.x AS x2,
DECODE(t1.x, t2.x, 1, 0) AS not_distinct
FROM t t1, t t2
ORDER BY 1, 2
Yields:
X1 | X2 | NOT_DISTINCT
-----+------+-------------
1 | 1 | 1
1 | 2 | 0
1 | null | 0
2 | 1 | 0
2 | 2 | 1
2 | null | 0
null | 1 | 0
null | 2 | 0
null | null | 1
You could emulate IS DISTINCT FROM by using NOT EXISTS combined with INTERSECT:
plus_bor.curr_ssn IS DISTINCT FROM ga.plus_brwr_ssn
<=>
NOT EXISTS (SELECT plus_bor.curr_ssn FROM dual INTERSECT
SELECT ga.plus_brwr_ssn FROM dual);
Example:
WITH cte(a,b) AS (
SELECT 1, NULL FROM dual UNION ALL
SELECT 1,2 FROM dual UNION ALL
SELECT 1,1 FROM dual UNION ALL
SELECT NULL, 1 FROM dual UNION ALL
SELECT NULL, NULL FROM dual
)
SELECT *
FROM cte
WHERE NOT EXISTS (SELECT a FROM dual INTERSECT
SELECT b FROM dual)
Rextester Demo
Output:
A B
------------
1 NULL
1 2
NULL 1
And in your case IS NOT DISTINCT FROM is simply EXISTS:
plus_bor.curr_ssn IS NOT DISTINCT FROM ga.plus_brwr_ssn
<=>
EXISTS (SELECT plus_bor.curr_ssn FROM dual INTERSECT
SELECT ga.plus_brwr_ssn FROM dual);
Example:
WITH cte(a,b) AS (
SELECT 1, NULL FROM dual UNION ALL
SELECT 1,2 FROM dual UNION ALL
SELECT 1,1 FROM dual UNION ALL
SELECT NULL, 1 FROM dual UNION ALL
SELECT NULL, NULL FROM dual
)
SELECT *
FROM cte
WHERE EXISTS (SELECT a FROM dual INTERSECT
SELECT b FROM dual);
Output:
A B
1 1
NULL NULL
Rextester Demo2
ADDENDUM
This approach has one big advantage over COALESCE/NVL approach as proposed in comments.
You don't have to think about default neutral value dependent on datatype.
For example if column is datatype DATE/INT/TEXT then you have to write something like:
coalesce(col1,DATE '1900-01-01') = coalesce(col2,DATE '1900-01-01')
coalesce(col1, 0) = coalesce(col2, 0)
coalesce(col1, ' ') = coalesce(col2, ' ')
There is of course slight chance of collision. For example:
coalesce(col1, 0) = coalesce(col2, 0)
=>
col1 = NULL
col2 = 0
and we have incorrect match!!!

How to do select count(*) group by and select * at same time?

For example, I have table:
ID | Value
1 hi
1 yo
2 foo
2 bar
2 hehe
3 ha
6 gaga
I want my query to get ID, Value; meanwhile the returned set should be in the order of frequency count of each ID.
I tried the query below but don't know how to get the ID and Value column at the same time:
SELECT COUNT(*) FROM TABLE group by ID order by COUNT(*) desc;
The count number doesn't matter to me, I just need the data to be in such order.
Desire Result:
ID | Value
2 foo
2 bar
2 hehe
1 hi
1 yo
3 ha
6 gaga
As you can see because ID:2 appears most times(3 times), it's first on the list,
then ID:1(2 times) etc.
you can try this -
select id, value, count(*) over (partition by id) freq_count
from
(
select 2 as ID, 'foo' as value
from dual
union all
select 2, 'bar'
from dual
union all
select 2, 'hehe'
from dual
union all
select 1 , 'hi'
from dual
union all
select 1 , 'yo'
from dual
union all
select 3 , 'ha'
from dual
union all
select 6 , 'gaga'
from dual
)
order by 3 desc;
select t.id, t.value
from TABLE t
inner join
(
SELECT id, count(*) as cnt
FROM TABLE
group by ID
)
x on x.id = t.id
order by x.cnt desc
How about something like
SELECT t.ID,
t.Value,
c.Cnt
FROM TABLE t INNER JOIN
(
SELECT ID,
COUNT(*) Cnt
FROM TABLE
GROUP BY ID
) c ON t.ID = c.ID
ORDER BY c.Cnt DESC
SQL Fiddle DEMO
I see the question is already answered, but since the most obvious and most simple solution is missing, I'm posting it anyway. It doesn't use self joins nor subqueries:
SQL> create table t (id,value)
2 as
3 select 1, 'hi' from dual union all
4 select 1, 'yo' from dual union all
5 select 2, 'foo' from dual union all
6 select 2, 'bar' from dual union all
7 select 2, 'hehe' from dual union all
8 select 3, 'ha' from dual union all
9 select 6, 'gaga' from dual
10 /
Table created.
SQL> select id
2 , value
3 from t
4 order by count(*) over (partition by id) desc
5 /
ID VALU
---------- ----
2 bar
2 hehe
2 foo
1 yo
1 hi
6 gaga
3 ha
7 rows selected.