Add new column to a table with a value group by value - sql

I have a Circus table as follow
circus_id
circus_date
circus_show_price
1
09-12-2020
78
2
12-01-2021
82
and a Ticket table as follow
ticket_id
circus_id
ticket_category
1
1
Adult
2
1
Student
3
1
Children
4
2
Adult
5
2
Children
6
2
Adult
and i want to alter the circus table by adding a new column called ticket_sold and the value should be as follow
circus_id
circus_date
circus_show_price
ticket_sold
1
09-12-2020
78
3
2
12-01-2021
82
3
this is what I have tried
alter table circus add ticket_sold numeric(3) default 0;
update circus set ticket_sold = (select count(ticket_id) from ticket group by circus_id);
it gives me an error said
single-row subquery returns more than one row

In-general, don't, as you will end up with a ticket_sold column that rapidly becomes out-of-sync with the ticket table.
If you want to have a dynamically updating column then:
1. Use a view.
You can just compute the value whenever you need it:
CREATE VIEW circus_view (circus_id, circus_date, circus_show_price, tickets_sold) AS
SELECT c.circus_id,
c.circus_date,
c.circus_show_price,
(SELECT COUNT(*) FROM ticket t WHERE t.circus_id = c.circus_id)
FROM circus c;
2. Use a trigger.
If you must persist the number of tickets in the circus table then:
ALTER TABLE Circus ADD tickets_sold NUMBER;
CREATE TRIGGER circus_tickets
AFTER INSERT OR UPDATE OR DELETE ON Ticket
BEGIN
UPDATE Circus c
SET tickets_sold = (SELECT COUNT(*) FROM ticket t WHERE t.circus_id = c.circus_id);
END;
/
fiddle

Is is not group by clause you need because query then returns number of tickets per each circus, but - then you get as many rows as there are circus_ids in the ticket table. Instead, correlate subquery to the main table:
update circus c set
c.ticket_sold = (select count(t.ticket_id)
from ticket t
where t.circus_id = c.circus_id
);

Related

Assign explicit version to existing rows of the table

I have a table where records are inserted and updated. In case of updates, a new row is inserted into the table. In order to track updates for a given record, there's a column added to the table called root_record_id which holds the id of the very first record in the update chain.
For eg: Consider the record table schema as follows:
id
root_record_id
other columns
1
1
...
2
2
...
3
1
...
4
1
...
5
2
...
In this case, a record with id=1 was inserted, which was then updated to id=3 and then to id=4. Similarly the record with id=2 was inserted and then updated to id=5.
I want to add a version column to this table, where version is incremented on each update and starts with 0.
id
root_record_id
version
other columns
1
1
0
...
2
2
0
...
3
1
1
...
4
1
2
...
5
2
1
...
I tried writing queries using group by clause on root_record_id but failed to accomplish the task.
If you are looking for the general sequence on how to add the column and then pre-fill the values, then follow this fiddle: https://dbfiddle.uk/?rdbms=sqlserver_2019&fiddle=5a04b49fbda3883a9605f5482e252a1b
Add the version column allowing nulls:
ALTER TABLE Records ADD version int null;
Update the version according to your logic:
UPDATE Records
SET version = lkp.version
FROM Records r
INNER JOIN (
SELECT Id, COUNT(root_record_id) OVER (partition by root_record_id ORDER BY id ASC)-1 as version
FROM Records
) lkp ON r.Id = lkp.Id;
Alter the version column to NOT allow nulls
ALTER TABLE Records ALTER COLUMN version int not null;
Finally, ensure that you increment the version column during new row inserts.
DBFIDDLE
This query produces the version that you can use (in an update, or in a trigger):
SELECT
id,
root_record_id,
RANK() OVER (partition by root_record_id ORDER BY id ASC)-1 version
FROM table1
ORDER BY id;
output:
id
root_record_id
version
1
1
0
2
2
0
3
1
1
4
1
2
5
2
1

PostgreSQL trigger which updates count in another table

I am rather new to PostgreSQL and I am really stuck with an apparently simple task. I have a table with music albums and a table with bands features, including how many albums such band released in a specific time, let say between 1990 and 1995. So I need to create a trigger that keeps the bands tabe updated whenever I insert, delete or updaste the albums table.
These are the tables:
Album
id_album id_band year
1 1 1995
2 1 1985
3 2 1993
4 3 1998
Band
id_band num_albums9095
1 1
2 1
3 0
So I have created the following function and trigger:
CREATE FUNCTION update_num_nineties()
RETURNS trigger AS $$ BEGIN
UPDATE band
SET num_albums_eighties = (SELECT COUNT (*) FROM album
where album.year between 1990 and 1995
GROUP BY album.id_band);
END;
$$LANGUAGE plpgsql;
CREATE TRIGGER update_nineties_album_mod_album AFTER UPDATE ON album FOR EACH row EXECUTE PROCEDURE update_num_nineties();
But I would get a subquery used as an expression returned more than one row message anytime I try to update any value to test it.
Would anyone be so kind to help me see why I am goind in the wrong direction?
You need to correlate the subquery with the outer query:
update band b
set num_albums_eighties = (
select count (*)
from album a
where a.year between 1990 and 1995 and a.id_band = b.id_band
) --^-- correlation --^--
While this technically works, it is still rather inefficient, because it resets the whole table when just one row in modified. You can restrict the rows with a where clause:
update band b
set num_albums_eighties = (
select count (*)
from album a
where a.year between 1990 and 1995 and a.id_band = b.id_band
)
where b.id_band in (old.band_id, new.band_id)

Trigger to prevent update in postgresql

I want to create a trigger which will prevent update in a row from that table if that entry is present in another table too.
example
Table M
m_id title
1 abc
2 def
3 ghi
Table N
n_id m_id
1 2
2 3
so if try to update values 2 or 3 in table m it shouldn't allow that
whereas if i try to update value 1 from table it should do so.
You'll have to create a trigger BEFORE UPDATE ON m FOR EACH ROW that checks IF OLD.m_id <> NEW.m_id AND EXISTS (SELECT 1 FROM n WHERE n.m_id = OLD.m_id) and throws an exception in that case.

Easiest way to update the ids of rows in sql server?

the primary key ID values in this table are being used in our 2 systems that were recently merged, however there is a large number of items in one of the systems that are pointing to the wrong id values, i need to update the ID(PK) values so that the 6 million existing items will be pointing to the correct row.
id like to update the id columns to the following:
ID
1 to 5
2 to 6
3 to 7
4 to 1
5 to 2
6 to 3
7 to 4
Well, assuming it is not an IDENTITY column (in which case you'll need to set IDENTITY_INSERT to on) then the following should work (see SQLFiddle for example)
UPDATE MyTable
SET ID =
CASE WHEN ID >= 4 SET ID - 3
ELSE ID + 4
END
Use update query with a case statement
Update tableName set PkId = Case PkId
When 1 then 5
When 2 then 6
When 3 then 7
When 4 then 1
When 5 then 2
When 6 then 3
When 7 then 4 End
Where PkId In (1,2,3,4,5,6,7)
If the values in your answer aer just a small subset of the values that need to be change (Do all 6 million need to change?), then you need to Create a mapping table that has the old incorrect value and the new correct value, and use that (with a join) instead of the case statement.
Update t set PkId = map.NewPkId
From tablename t
Join mappingTable m
On m.oldPkId = t.PkId

Oracle SQL: update table conditionally based on values in another table

[Previous essay-title for question]
Oracle SQL: update parent table column if all child table rows have specific value in a column. Update RANK of only those students who have 100 marks in all the subjects. If student has less than 100 marks in any subject, his RANK should not be updated.
I have a scenario where I have a parent table and a child table. The child table has a foreign key to parent table. I need to update parent table's status column when a column in child table rows have specific values. There are more than one child records for each parent, in some cases none. Is it possible to achieve this with Oracle SQL, without using PL/SQL. Is that possible, can some one explain how? In some case I have to update parent table row's column based on two columns of child table records.
My exact problem is like : I have two tables STUDENTS, MARKS. MARKS has a FK to STUDENTS named STUDENT_ID.MARKS has number of rows for a STUDENT record, depending on different subjects (MARKS has a FK to SUBJECTS), and has a column named MARKS_OBTAINED. I have to check that if MARKS_OBTAINED for one student for every subject (i.e. all his records in MARKS) have value 100, then update STUDENT table's column RANK to a value 'Merit'. This query:
update STUDENT
set RANK = 'Merit'
where exists ( select *
from MARKS
where MARKS.STUDENT_ID = STUDENT.ID
and MARKS.MARKS_OBTAINED = 100)
and not exists ( select *
from MARKS
where MARKS.STUDENT_ID = STUDENT.ID
and MARKS.MARKS_OBTAINED != 100)
updates all those student who have 100 marks in any subject. It does not exclude records which have non 100 marks. Because it passes rows for a STUDENT in MARKS where one record in MARKS has 100 MARKS_OBTAINED but other records have less than 100 marks, but since STUDENT obtained 100 marks in one subject, its RANK will also get updated. The requirement is that if any STUDENT records has a MARKS record with non 100 value in MARKS_OBTAINED column this STUDENT record should get excluded from the query.
Total rewrite
This is a complete rewrite to fit my example to the OQ's revised question. Unfortunately Manish has not actually run my original solution otherwise they would realise the following assertion is wrong:
Your solution returns all those
student who have 100 marks in any
subject. It does not exclude records
which have non 100 marks.
Here are six students and their marks.
SQL> select * from student
2 /
ID RANK
---------- ----------
1 normal
2 normal
3 normal
4 normal
5 normal
6 normal
6 rows selected.
SQL> select * from marks
2 /
COURSE_ID STUDENT_ID MARK
---------- ---------- ----------
1 1 100
2 1 100
1 2 100
2 2 99
1 4 100
2 5 99
1 6 56
2 6 99
8 rows selected.
SQL>
Student #1 has two courses with marks of 100. Student #4 has just the one course but with with a mark of 100. Student #2 has a mark of 100 in one course but only 99 in the other course they have taken. None of the other students scored 100 in any course. Which students will be awarded a 'merit?
SQL> update student s
2 set s.rank = 'merit'
3 where exists ( select null
4 from marks m
5 where m.student_id = s.id
6 and m.mark = 100 )
7 and not exists ( select null
8 from marks m
9 where m.student_id = s.id
10 and m.mark != 100)
11 /
2 rows updated.
SQL>
SQL> select * from student
2 /
ID RANK
---------- ----------
1 merit
2 normal
3 normal
4 merit
5 normal
6 normal
6 rows selected.
SQL>
And lo! Only those students with 100 marks in all their courses have been updated. Never underestimate the power of an AND.
So the teaching is: an ounce of testing is worth sixteen tons of supposition.
Your question is a little too vague at the moment to really answer fully. What happens to a parent row if it has no children? What happens if some of the child rows have specific values but not all of them? In the two-column case, what combinations of number of children/values are needed (is is the same set of values for each column or unique ones? Is it an AND relationship or an OR relationship)? Etc...
Anyway, making the assumption that there needs to be at least one child row with a value in a given domain, this should be fairly straightforward:
update PARENT set STATUS = 'whatever'
where ID in (
select parent_id from CHILD
where value_col in ('your', 'specific', 'values', 'here')
);
This general pattern expands to the multi-column case easily (just add an extra AND or ORed condition to the inner where clause), and to the negative case too (change where ID in to where ID not in).
If performance of this update is an issue you may want to look at triggers - at the price of slightly slower inserts on the child tables, you can keep your parent table up-to-date on an ongoing basis without having to run this update statement periodically. This works quite nicely because the logic of inspecting each child row is essentially distributed across each individual insert or update on the child table. Of course, if those child modifications are performance-critical, or if the child changes many times in between the points where you need to update the parent, then this wouldn't work very well.
What about:
UPDATE ParentTable
SET StatusColumn = 78
WHERE PK_Column IN
(SELECT DISTINCT FK_Column
FROM ChildTable AS C1
WHERE (SELECT COUNT(*) FROM ChildTable C2
WHERE C1.FK_Column = C2.FK_Column) =
(SELECT COUNT(*) FROM ChildTable C3
WHERE C1.FK_Column = C3.FK_Column
AND C3.OtherColumn = 23)
)
I strongly suspect there are neater ways to do it, but...the correlated sub-queries count the number of rows in the child table for a particular parent and the number of rows in the child table for the same parent where some filter condition matches a particular value. Those FK_Column values are returned to the main UPDATE statement, giving a list of primary key values for which the status should be updated.
This code enforces the stringent condition 'all matching rows in the child table satisfy the specific condition'. If your condition is simpler, your sub-query can be correspondingly simpler.