Update a table based on a results of a group by - sql

Update a table based on a results of a group by
I've got a tricky update problem I'm trying to solve. There are two tables that contain the same three columns plus additional varied columns, looking like this:
Table1 {pers_id, loc_id, pos, ... }
Table2 {pers_id, loc_id, pos, ... }
None of the fields are unique. The first two fields collectively identify the records in a table (or tables) as belonging to the same entity. Table1 could have 15 records belonging to an entity, and table2 could have 4 records belonging to the same entity. The third column 'pos' is an index from 0 to whatever, and this is the column that I'm trying to update.
In Table1 and in Table2, the pos column begins at 0, and increments based on user selection, so that in the example (15 records in table1 and 4 records in table2), table1 contains 'pos' values of 0 - 14, and Table2 contains 'pos' values of 0-3.
I want to increment the pos field in Table1 with the results of the count of similar entities in Table2. This is the sql statement that correctly gives me the results from table2:
select table2.pers_id, table2.loc_id, count(*) as pos_increment from table2 group by table2.pers_id, table2.loc_id;
The end result of the update, in the example (15 records in table1 and 4 records in table2), would be all records in Table1 of the same entity being incremented by 4 (the result of the specific entity group by). 0 would be changed to 4, 15 to 19, etc.
Is this achievable in a single statement?

Since you only need to increment the pos field the solution is really simple:
update table1 t1
set t1.pos = t1.pos +
(select count(1)
from table2 t2
where t2.pers_id = t1.pers_id
and t2.loc_id = t1.loc_id)

Yes, this is possible, you can use MERGE for some of these upadtes and there are ways to relate values between the update and the subselect. I have done this in the past, but it's tricky and I don't have an existing example.
You can find several examples on this site, some for Oracle and some for other database that will awork with slight modifications.

Related

Get the "most" optimal row in a JOIN

Problem
I have a situation in which I have two tables in which I would like the entries from table 2 (lets call it table_2) to be matched up with the entries in table 1 (table_1) such that there are no duplicates rows of table_2 used in the match up.
Discussion
Specifically, in this case there are datetime stamps in each table (field is utcdatetime). For each row in table_1, I want to find the row in table_2 in which has the closed utcdatetime to the table 1 utcdatetime such that the table2.utcdatetime is older than the table_1 utcdatetime and within 30 minutes of the table 1 utcdatetime. Here is the catch, I do not want any repeats. If a row in table 2 gets gobbled up in a match on an earlier row in table 1, then I do not want it considered for a match later.
This has currently been implemented in a Python routine, but it is slow to iterate over all of the rows in table 1 as it is large. I thought I was there with a single SQL statement, but I found that my current SQL results in duplicate table 2 rows in the output data.
I would recommend using a nested select to get whatever results you're looking for.
For instance:
select *
from person p
where p.name_first = 'SCCJS'
and not exists (select 'x' from person p2 where p2.person_id != p.person_id
and p.name_first = 'SCCJS' and p.name_last = 'SC')

MS SQL - Two tables, simple flag calculation to check if a value is present in another table

Long time viewer and my first question. Please be gentle.
I am having issues writing a query that incorporates tables with 1-1 / 1-M relationships.
To keep it simple - I have two tables
Tables
Query - Provide the entire list of cases from Table 1 and add a new column that has a flag (Y/N) if case has a car from table 2 whilst keeping the 1-1 relationship
Outputs
Try using exists logic to check, for each table 1 record, if it has a matching car record in the second table:
SELECT
t1.caseno,
CASE WHEN EXISTS (SELECT 1 FROM Table2 t2
WHERE t1.caseno = t2.caseno AND t2.Product = 'Car')
THEN 'Y' ELSE 'N' END AS car_flag
FROM Table1 t1
ORDER BY
t1.caseno;
Demo

SQL deleting one of two duplicate records?

I have a DB that has a problem that there are two of the same records for everything but they all have a different ID, but they have 2 columns (the actual data) that are the same. I was wondering if there was a good way to have a DELETE statement where I could select all these records that have the 2 columns match but have a different ID and delete one (doesn't matter which one)?
If you could could you give me a code example?
Delete from ...
where id in (select max(id), count as c
from ...
group by data1, data2
having c >1)
The idea is to select the bigger id of all duplicate rows, by grouping the rows on the column that are the same and making sure that there are multiple rows (having clause).
delete from your_table
where id not in
(
select min(id)
from your_table
group by col2
)

SQL Query relating to grouping entries

When I normalised my database, I used a text value to group together entries giving them the same foreign key. However, I also had 2 other fields prior to normalisation which used reference numbers to group together entries, one into pairs of entries and one into groups of entries. Because these grouped entries did not necessarily have the same text value, some entries will have been left out of the grouping (shared foreign key id).
I need a query which selects all entries which share a PairRef or GroupRef where the entries in that group do not all have the same ForeignKeyID.
Example:
ID PairRef GroupRef ForeignKeyID TextValue (in linked table2)
1 25 25 123 'Text value 123'
2 25 25 255 'Text value 255'
3 1 50 201 'Text value 201'
4 1 50 201 'Text value 201'
5 2 50 202 'Text value 202'
6 2 50 202 'Text value 202'
7 3 50 203 'Text value 203'
8 3 50 203 'Text value 203'
I then need to be able to edit the data to group them together. The problem is that in order to do this, I would need the query to be from more than one table because I need to see the text associated with the foreign key. I have found that using phpMyAdmin, although I can create queries from more than one table using inner joins, the results of these queries cannot be edited in the way that queries from one table can.
I guess the alternative would be to do an update query on the query results. Could you give an example of a quick and easy way of doing an update query on query results, without losing the original query which needs to be used repeatedly.
In the above example, for the regrouping of ID 1 and ID 2 which share the same PairRef, I would need to physically look at TextValue 123 and 255 and depending which one was the more appropriate text label, I would decide on which entry to change. Let's say that 'Text value 123' was the value I wanted to retain for that grouping. I would update ID 2 to ForeignKeyID 123, which would obviously automatically change the TextValue for that entry to 'Text value 123'.
For the regrouping of IDs 3 to 8, which share the same GroupRef, if I decided after looking at the data to re-group them all as 'Text value 201', I would change IDs 5,6,7 and 8 to ForeignKeyID = 201, which would automatically change all the TextValues for those entries to 'Text Value 201'.
IDs 1 to 8 would then no longer appear on the query results because the grouping problem would have been resolved and they would no longer meet the query criteria.
I need to find the easiest way possible of doing this, as grouping entries together is one of the main purposes of the databases and there is a lot of this editing to do.
Thank you
For the first part (select all entries which share a PairRef or GroupRef where the entries in that group do not all have the same ForeignKeyID), the following query can be used. It groups by PairRef and selects PairRefs which have more than 1 distinct ForeignKeyID. Then, all entries which have these PairRefs are selected. Similarly, the data is grouped by GroupRef also. All GroupRefs which have more than 1 distinct ForeignKeyID are selected. Then, all entries which have these GroupRefs are selected.
SELECT
T1.*
FROM Table1 T1
INNER JOIN Table2 T2
ON T1.ForeignKeyID = T2.ForeignKeyID
WHERE PairRef IN
(
SELECT
PairRef
FROM table1
GROUP BY PairRef
HAVING Count(DISTINCT ForeignKeyID) > 1
)
OR GroupRef IN
(
SELECT
GroupRef
FROM table1
GROUP BY GroupRef
HAVING Count(DISTINCT ForeignKeyID) > 1
);
For the second part (edit the data to group them together), I do not understand why you would need to see the TextValue from table2 (if it is corresponding to ForeignKeyID in table1). Anyway, once you have seen the PairRefs / GroupRefs, which have different ForeignKeyID values, you can run an update statement for each PairRef / GroupRef, since it seems like a manual process.
UPDATE Table1
SET ForeignKeyID = <ForeignKeyID to be set>
WHERE PairRef = <PairRef to update>;
UPDATE Table1
SET ForeignKeyID = <ForeignKeyID to be set>
WHERE GroupRef = <PairRef to update>;
You may need to run the first query again to check the data, because the UPDATE query for GroupRef might result in different values for PairRef.
Here is a SQL Fiddle demo. Thank you, #JohnLBevan for the stub.

How do I find the most recently dated record for each ID in a set of IDs where any ID can have multiple records each for a particular date?

I've got a data set that looks as follows:
The first column is an auto-increment primary key. The second column is an ID number for whatever, maybe 3 rocks with IDs 1, 2 and 3 respectively. (I probably should have used the standard customer and order example but oh well.) The third column is a date when I threw the rock. I track the date each time the rock is thrown, hence the multiple IDs (the second column) each with a throwing time.
I want a query to return the rock ID and most recent date for each ID. The result of course would have a single record for each ID - the one with the latest access time.
I'm struggling with the possible combination of "DISTINCT", "TOP 1" and "GROUP BY" clauses that gives the result I want.
SELECT id, max(date)
FROM table
GROUP BY id
If you want also the autoincrement row id then
SELECT t1.rowid, t1.id, t1date
FROM table t1
JOIN (SELECT id,max(date) date FROM table1 t2 GROUP BY id) t2 ON t1.id = t2.id AND t1.date = t2.date