Set Insert trigger to store max value in another column - sql

This is what my table looks like:
student_id
subject_id
total
max
101
1
6
102
2
5
103
1
9
101
1
10
103
2
2
104
1
7
I want the "max" column to be automatically populated when the total is inserted.
Expected Output:
student_id
subject_id
total
max
101
1
10
10
102
2
5
7
103
1
9
10
101
1
8
10
103
2
2
7
104
1
7
10
I will like to create a trigger for this.
This is my SELECT statement which works fine but how do I put it in a trigger?
WITH CTE AS (SELECT `subject_id`,MAX(`total`) AS MaxTotal
FROM results
GROUP BY `subject_id`
)
SELECT results.*,CTE.MaxTotal
FROM results
JOIN CTE ON results.`subject_id` = CTE.`subject_id`;
I did this but I got a plethora of errors
CREATE TRIGGER `max_score_before_INSERT` BEFORE INSERT ON `results`
FOR EACH ROW
SET NEW.max = (WITH CTE AS (SELECT `subject_id`,MAX(`NEW.total`) AS MaxTotal
FROM results
GROUP BY `subject_id`
)
SELECT results.*,CTE.MaxTotal
FROM results
JOIN CTE ON results.`subject_id` = CTE.`subject_id`
);

You can't reference "new" inside a BEFORE trigger type. You may want to first
insert the row, then update the column with an AFTER trigger type:
CREATE TRIGGER `max_score_before_INSERT` AFTER INSERT ON `results`
FOR EACH ROW
UPDATE <your_table_name>
INNER JOIN (SELECT subject_id,
MAX(total) AS total_max
FROM <your_table_name>
GROUP BY subject_id) cte
ON <your_table_name>.subject_id = cte.subject_id
SET <your_table_name>.max = cte.total_max;
Note that this approach will update the previous columns too, as if you insert a new field that becomes the max, you may want to update the already existing rows too. If not, then you can use a condition inside the UPDATE statement:
CREATE TRIGGER `max_score_before_INSERT` AFTER INSERT ON `results`
FOR EACH ROW
UPDATE <your_table_name>
INNER JOIN (SELECT subject_id,
MAX(total) AS total_max
FROM <your_table_name>
GROUP BY subject_id) cte
ON <your_table_name>.subject_id = cte.subject_id
SET <your_table_name>.max = cte.total_max;
WHERE <your_table_name>.student_id = NEW.student_id
AND <your_table_name>.subject_id = NEW.subject_id;

Related

Find Duplicate in column b for each value in column A Oracle

I have Table like Below:
Sr_No C_A C_B
-------------------
1 100 A
2 100 A
3 100 B
4 101 A
5 102 A
6 102 B
7 103 A
8 103 A
9 103 B
And I want select query to get below
C_A
----
100
103
I want to know how many records are in C_A column having duplicate values in C_B column.
The query below checks, for each C_A group, that the number of distinct C_B values is less than the total number of values. This condition implies that all C_B values are not unique for a given C_A group, and that there are duplicates.
SELECT C_A
FROM yourTable
GROUP BY C_A
HAVING COUNT(DISTINCT C_B) < COUNT(*)
If I understand well, you may need:
select distinct C_A
from
(select C_A, C_B, count(1) over ( partition by C_A, C_B) as cnt
from test
)
where cnt > 1
The nested query counts the number of duplicates for each couple of values in C_A, C_B, while the external one simply filters this result to only get couples with duplicates

PSQL get duplicate row

I have table like this-
id object_id product_id
1 1 1
2 1 1
4 2 2
6 3 2
7 3 2
8 1 2
9 1 1
I want to delete all rows except these-
1 1 1
4 2 2
6 3 2
9 1 2
Basically there are duplicates and I want to remove them but keep one copy intact.
what would be the most efficient way for this?
If this is a one-off then you can simply identify the records you want to keep like so:
SELECT MIN(id) AS id
FROM yourtable
GROUP BY object_id, product_id;
You want to check that this works before you do the next thing and actually throw records out. To actually delete those duplicate records you do:
DELETE FROM yourtable WHERE id NOT IN (
SELECT MIN(id) AS id
FROM yourtable
GROUP BY object_id, product_id
);
The MIN(id) obviously always returns the record with the lowest id for a set of (object_id, product_id). Change as desired.

Is there a way to update groups of rows with separate incrementing values in one query

Lets say you have the following table:
Id Index
1 3
1 1
2 1
3 3
1 5
what I would like to have is the following:
Id Index
1 0
1 1
2 0
3 0
1 2
As you might notice, the goal is for every row where Id is the same, to incrementally update the Index column, starting from zero.
Now, I know this is fairly simple with using cursors, but out of curiosity is there a way to do this with single UPDATE query, somehow combining with temp tables, common table expressions or something similar?
Yes, assuming that the you don't really care about the order of the values for the new index values. SQL Server offers updatable CTEs and window functions that do exactly what you want:
with toupdate as (
select t.*, row_number() over (partition by id order by (select NULL)) as newindex
from table t
)
update toupdate
set index = newindex;
If you want them in a specific order, then you need another column to specify the ordering. The existing index column doesn't work.
With Row_number() -1 and CTE you can write as:
CREATE TABLE #temp1(
Id int,
[Index] int)
INSERT INTO #temp1 VALUES (1,3),(1,1),(2,1),(3,3),(1,5);
--select * from #temp1;
With CTE as
(
select t.*, row_number() over (partition by id order by (select null))-1 as newindex
from #temp1 t
)
Update CTE
set [Index] = newindex;
select * from #temp1;
Demo
I'm not sure why you would want to do this really, but I had fun figuring it out!
This solution relies on your table having a primary key for the self join... but you could always create an auto inc index if none exists and this is a one off job... This will also have the added benefit of getting you to think about the precise ordering of this you want... as currently there is no way of saying which order [ID] will get [Index] in.
UPDATE dbo.Example
SET [Index] = b.newIndex
FROM dbo.Example a
INNER JOIN (
select
z.ID,
z.[Index],
(row_number() over (partition by ID order by (select NULL))) as newIndex
from Example z
) b ON a.ID = b.ID AND a.[Index]=b.[Index] --Is this a unique self join for your table?.. no PK provided. You might need to make an index first.
Probably, this is what you want
SELECT *,RANK() OVER(PARTITION BY Id ORDER BY [Index])-1 AS NewIndex FROM
(
SELECT 1 AS Id,3 [Index]
UNION
SELECT 1,1
UNION
SELECT 2,1
UNION
SELECT 3,3
UNION
SELECT 1,5
) AS T
& the result will come as
Now if you want to update the table then execute this script
UPDATE tblname SET Index=RANK() OVER(PARTITION BY t.Id ORDER BY t.[Index])-1
FROM tblname AS t
In case I am missing something or any further assistance is required please let me know.
CREATE TABLE #temp1(
Id int,
Value int)
INSERT INTO #temp1 VALUES (1,2),(1,3),(2,3),(4,5)
SELECT
Id
,Value
,ROW_NUMBER() OVER (PARTITION BY Id ORDER BY Id) Id
FROM #temp1
Start with this :)
Gave me results like
Id Value Count
1 2 1
1 3 2
1 2 3
1 3 4
1 2 5
1 3 6
1 2 7
1 3 8
2 3 1
2 4 2
2 5 3
2 3 4
2 4 5
2 5 6
2 4 7
2 5 8
2 3 9
2 3 10
3 4 1
4 5 1
4 5 2
4 5 3
4 5 4

SQL - Add value with previous row only

I have a table named myvals with the following fields:
ID number
-- -------
1 7
2 3
3 4
4 0
5 9
Starting on 2nd row, I would like to add the number with the previous row number. So, my end result would look like this
ID number
-- ------
1 7
2 10
3 7
4 4
5 9
You could use the LAG analytic function
SELECT Id, number + LAG(number,1,0) OVER (ORDER BY Id) FROM table
First thing's first. You can't add to null to ID 1 must have a value.
create table #temp
(
month_type datetime,
value int
)
insert into #temp
Select '2015/01/01',1
union
Select '2015/02/01',2
union
Select '2015/03/01',3
union
Select '2015/04/01',4
SELECT t.value,t1.value,(t.value+t1.value)/2 FROM #temp t1
left join #temp t on t.month_type=Dateadd(MONTH,-1,t1.month_type)

Update grouped records in Oracle with an incremental value

So a dilemna, I have an Oracle table called T_GROUP. The records in the table have a unique id (ID) and they are part of a Study, identified by STUDY_ID, so multiple groups can be in the same Study.
CREATE TABLE T_GROUP
(
"ID" NUMBER(10,0),
"GROUP_NAME" VARCHAR2(255 CHAR),
"STUDY_ID" NUMBER(10,0)
)
The existing table has hundreds of records and I now add a new column called GROUP_INDEX:
ALTER TABLE T_GROUP ADD (
GROUP_INDEX NUMBER(10,0) DEFAULT(0)
);
After adding the column I need to run a script to update the GROUP_INDEX field as such: it should start at 1 and increment by 1 for each group within a study, starting with the lowest ID.
So now I have data as follows:
ID GROUP_NAME STUDY_ID GROUP_INDEX
-------------------------------------------
1 Group 1 3 0
2 Group 2 3 0
3 My Group 5 0
4 Big Group 5 0
5 Group X 5 0
6 Group Z 6 0
7 Best Group 6 0
After the update the group_index field should be as follows:
ID GROUP_NAME STUDY_ID GROUP_INDEX
-------------------------------------------
1 Group 1 3 1
2 Group 2 3 2
3 My Group 5 1
4 Big Group 5 2
5 Group X 5 3
6 Group Z 6 1
7 Best Group 6 2
The update will be run from sqlplus via a batch file. I've played around with group by and sub queries but I'm not having much luck, and having never used sqlplus I'm not sure if I can use variables, cursors etc. All tips greatly appreciated!
You should be able to use the analytic function row_number for this
UPDATE t_group t1
SET group_index = (SELECT rnk
FROM (SELECT id,
row_number() over (partition by study_id
order by id) rnk
FROM t_group) t2
WHERE t2.id = t1.id)
Here is a version using the MERGE statement. Might be faster than the sub-select (but doesn't have to be).
merge into t_group
using
(
select id,
row_number() over (partition by study_id order by id) rnk
from t_group
) t on t.id = t_group.id
when matched then update
set group_index = t.rnk;
This assumes that id is the primary key (or at least unique)
I can't test it right now, so there might be some syntax error in it.