Can't figure out correct SQL query for historical table records - sql

I'm trying for hours to find out the correct SQL query to select the latest historcal record from a table (in MySQL).
In my application I'd like to keep a history of every data modification. So my idea was, instead to make an UPDATE to an existing record, I'd rather make an INSERT of a new record. Additionally there is a revision counter, which gets increased with each record modification.
This is my table:
uid rid created_by deleted revision username password admin
1 1 0 0 0 stefan abcdefg 1
2 2 1 0 0 maria bcdefgh 1
3 3 1 0 0 carl cdefghi 0
4 4 1 0 0 SUSANN ABC123 0
5 4 1 0 1 SUSANN 123ABC 0
6 4 1 0 2 SUSANN 123ABC 1
7 4 1 1 3 SUSANN 123ABC 1
Note the rows with uid 4 to 7 are actually the same record, namely of "SUSANN". Row 4 is the initial row. Row 5 modified tha password, row 6 modifies the admin-flag, row 7 modified the deleted-flag.
uid is an auto-incrementor and identifies the row in the table for internal purposes. rid is the actual record-ID.
Now. Selecting the most current revision of a single record could be done this way:
SELECT * FROM table
WHERE rid=4
ORDER BY revision DESC
LIMIT 1
My problem is selecting a list of all the latest revision of all logins: Based on the sample data the result set should be:
uid rid created_by deleted revision username password admin
1 1 0 0 0 stefan abcdefg 1
2 2 1 0 0 maria bcdefgh 1
3 3 1 0 0 carl cdefghi 0
7 4 1 1 3 SUSANN 123ABC 1
Could someone point me in the right direction. I think the right keywords would be sufficient already. From there I could probably figure out a way.
Thanks.

This should work:
SELECT t.*
FROM table t
JOIN (SELECT rid, MAX(revision) MaxRevision FROM table GROUP BY rid) mt
ON t.rid = mt.rid AND t.revision = mt.MaxRevision

Related

Oracle SQL index rows based on group by/ parent row

I have 1 table, which refers by value to rows in the same table
Example table:
ID PARENT_ID NAME
1 0 john
2 1 jane
3 2 smigy
4 2 gujo
5 1 duby
6 1 ruby
7 5 foo
8 2 bar
9 3 baz
10 3 qux
The root-parent has parent 0 (just so it wouldn't be null), in this case there's
1 root-parent - parent(0)=1.
root-parent has 1lvl children - parent(1)=2;5;6.
1lvl children has 2lvl children - parent(2)=3;4;8. parent(5)=7. parent(6) has nothing.
2lvl children has 3lvl children - parent(3)=9;10. parent(4) has nothing. parent(8) has nothing.
There is no lvl4 children or anything with depth beyond 4.
And I need to create a script (presumably SQL query - need to avoid function/procedure/etc.) that would index rows based on their position under their parent.
Just like if I'd select all root-parent's and get (rownum-1)
The goal table should look like this:
ID PARENT_ID NAME ROW_INDEX
1 0 john 0
2 1 jane 0
3 2 smigy 0
4 2 gujo 1
5 1 duby 1
6 1 ruby 2
7 5 foo 0
8 2 bar 2
9 3 baz 0
10 3 qux 1
I'm planing to add this column and thus the query will be executed only once. I've played by selecting seperate depth rows, but then I don't really know how to count inside/between group by (even if that is possible).
P.S. A better/good column name suggestion would also be very appreciated.
User row_number()
select mt.*, row_number() over(partition by parent_id order by id) - 1 as rn
from MyTable mt

How to arrange Sql rows in a specific format

I have table with up to 50 rows... like given below.
ID menu dispOdr ParntID
---------------------
1 abc 1 0
2 cde 2 0
3 fgh 1 2
4 ghdfdj 2 2
5 tetss 1 1
6 uni 3 0
but I want to be sorted
Like
ID menu dispOdr ParntID
---------------------
1 abc 1 0
5 tetss 1 1
2 cde 2 0
3 fgh 1 2
4 ghdfdj 2 2
6 uni 3 0
If have any query please let me know.. thanks in advance.
I am using sql server 2014
I think you need your current vs. desired output reversed. You say you want the menu column sorted, but it appears that it already is.
So assuming you are actually starting with the second table, you can sort the menu column simply using ORDER BY:
SELECT *
FROM mytable
ORDER BY menu ASC
I think that the query below produces the required output:
SELECT t1.ID, t1.menu, t1.dispOdr, t1.ParntID
FROM mytable AS t1
LEFT JOIN mytable AS t2 ON t1.ParntID = t2.ID
ORDER BY CASE
WHEN t1.ParntID = 0 THEN t1.dispOdr
ELSE t2.dispOdr
END,
CASE
WHEN t1.ParntID = 0 THEN 1
ELSE 2
END,
t1.dispOdr
The first CASE expression groups records according to the dispOdr of their parent. The second CASE places parent on the top of its subgroup. Finally, the last expression used in the ORDER BY clause orders all child records within a subgroup.
Note: The above query works with one level of nesting.

Matching two variables to create a new ID

I'm trying to create an SQL statement to match either an id number or a postcode and then assign a new id number
What I want to end up with is ‘newid’ that correctly recognizes that the first four records are the same person (even though the postcode for record 2 is different).
record id postcode newid
--------------------------
1 1 1 1
2 1 2 1
3 1 1 1
4 2 1 1
5 3 3 2
Any suggestions would be appreciated greatly.
Going based on your example:
SELECT RECORD,
(SELECT MIN (ID)
FROM users u2
WHERE users.id IN (u2.id, u2.postcode)
OR users.postcode in (u2.id, u2.postcode)
) AS newid
FROM users
This results with the following data:
RECORD NEWID
------------------
1 1
2 1
3 1
4 1
5 3
Here is the SQLFiddle

Inserting a new indicator column to tell if a given row maximizes another column in SQL

I currently have a table in SQL that looks like this
PRODUCT_ID_1 PRODUCT_ID_2 SCORE
1 2 10
1 3 100
1 10 3000
2 10 10
3 35 100
3 2 1001
That is, PRODUCT_ID_1,PRODUCT_ID_2 is a primary key for this table.
What I would like to do is use this table to add in a row to tell whether or not the current row is the one that maximizes SCORE for a value of PRODUCT_ID_1.
In other words, what I would like to get is the following table:
PRODUCT_ID_1 PRODUCT_ID_2 SCORE IS_MAX_SCORE_FOR_ID_1
1 2 10 0
1 3 100 0
1 10 3000 1
2 10 10 1
3 35 100 0
3 2 1001 1
I am wondering how I can compute the IS_MAX_SCORE_FOR_ID_1 column and insert it into the table without having to create a new table.
You can try like this...
Select PRODUCT_ID_1, PRODUCT_ID_2 ,SCORE,
(Case when b.Score=
(Select Max(a.Score) from TableName a where a.PRODUCT_ID_1=b. PRODUCT_ID_1)
then 1 else 0 End) as IS_MAX_SCORE_FOR_ID_1
from TableName b
You can use a window function for this:
select product_id_1,
product_id_2,
score,
case
when score = max(score) over (partition by product_id_1) then 1
else 0
end as is_max_score_for_id_1
from the_table
order by product_id_1;
(The above is ANSI SQL and should run on any modern DBMS)

SQL: Need to create two unique records for each single record

The simple question is how can you take a set of records with a PK and create exactly two records for each source with a slightly altered key for the duplicate? In other words, I take 4000 records and produce 8000 records where 4000 are identical and the other 4000 have a slightly altered key. I cannot do a union because this is essentially two selects (long story).
The rest gets complicated, but maybe necessary to provide examples.
This is my original set (it contains over 4000 records)
dateGroup areaGroup itemID editionID
1 1 1 1
1 1 1 2
1 2 1 1
1 2 2 1
2 1 1 1
2 1 1 2
2 2 1 1
2 2 1 2
For each record I need to create a duplicate record ganging the areaGroups together under '0', then create a comma separated list of original areaGroups as a separate field. (The "why" is some dumb programmer (me) made a mistake about 15 years ago.) I can renumber the editionIDs as necessary, but the original and duplicate record must have the same editionID (thus why a union wouldn't work). The PK remains the same as above (all fields)
dateGroup areaGroup itemID editionID aGroups
1 0 1 1 1
1 0 1 2 1
1 0 1 1 2 // Duplicate (EditionID)
1 0 2 1 2
2 0 1 1 1
2 0 1 2 1
2 0 1 1 2 // Duplicate (EditionID)
2 0 1 2 2
The end result would renumber the editionID as needed to make the record unique.
dateGroup areaGroup itemID editionID aGroups (EditionID is what is altered)
1 0 1 1 1
1 0 1 2 1
1 0 1 2 2 1 changed to 2 (one more than row 1)
1 0 2 1 2
2 0 1 1 1
2 0 1 2 1
2 0 1 2 2 1 changed to 2 (one more than row 1)
2 0 1 2 2
1 1 1 1
1 1 1 2
1 2 1 2 1 changed to 2 (editionID) to match
1 2 2 1
2 1 1 1
2 1 1 2
2 2 1 2 1 changed to 2 to match above
2 2 1 2
I know you could calculate the editionID like a row rank like so:
select row_number() over (
partition by dateGroup, itemID
order by dateGroup, itemID) as editionID
So all I need is to know how to duplicate the records from a single set
do a cross join on a derived table:
( select 1 as aGroups union all select 2 )
I'd create a temporary table with duplicates and their count.
Then I'd filter the original table to have only unique rows, and insert another row for each row in the temporary table, incrementing their editionID.
In MySQL, I'd use user #variables; not sure about MS SQL.
Did you try UNION ALL instead of just UNION
UDPATE perhaps I misunderstood the problem and I thought you were having a problem with the union loosing the duplicates.
If the problem is that you want to do a row_number over a union why don't you do somthing like
select row_number() over (
partition by dateGroup, itemID
order by dateGroup, itemID) as editionID
FROM
(
SELECT
dateGroup, itemID
FROM TableA
UNION ALL
SELECT
dateGroup, itemID
FROM TableB
) Data