I am searching for exactly the opposite of what Jonathan was searching in this example:
How to select multiple rows by multi-column primary key in MySQL?
Having 3 columns as a primary key (the 3rd is a date), I want to select all of them without the most recent one. And if there is no second entry for a combination of the first two primary values, I don't want to select it at all. Think of it as a kind of versioning. The table-structure contains more columns than those three and i want to select the whole rows.
Looks something like that:
{ID1 | ID2 | DATE} | more columns ...
Pseudocode:
SELECT * FROM table WHERE (first and second primary value are the same and exist more than once) AND NOT MAX(date)
:D
I want to output the data of all previous versions of the row, not including the most recent one.
Thanks in advance for any suggestions!
Break down the problem into steps:
Pseudo logic:
Get a data set with the records we want to exclude
now exclude that data set from the entire set
Step 1: Get a dataset of only those records having a max data for ID1, ID2
SELECT ID1, ID2, Max(date) date
FROM Table
GROUP BY ID1, ID2
Step 2: Now use that data set to identify/eliminate the records you don't want.. a not exists is likely the fastest.
Faster...
SELECT A.*
FROM TABLE A
WHERE NOT EXISTS
(SELECT 1
FROM (SELECT ID1, ID2, Max(date) date
FROM Table
GROUP BY ID1, ID2) B
WHERE A.ID1 = B.ID1
and A.ID2 = B.ID2
and A.Date = B.Date)
or as a self outer join on a subset, slower but gives you access to additional details on subset if needed. (not much use in this example but could be useful in other circumstances)
The left join to the data set shows those that match on the max date, so all other records would be null, which is the data set you're after...
SELECT A.*
FROM TABLE A
LEFT JOIN (SELECT ID1, ID2, Max(date) date
FROM Table
GROUP BY ID1, ID2) B
on A.ID1 = B.ID1
and A.ID2 = B.ID2
and A.Date = B.Date
WHERE B.ID1 is null
Related
I have two tables, call them Table1 and Table2. Table1 has a primary key of "ID" and Table2 has a foreign key field called "Table1ID".
I can run this join, but it will only work the way I want it to when there is a matching primary and foreign key value in both tables.
SELECT a.*, sum(b.Time) AS Time FROM Table1 AS a JOIN Table2 AS b ON a.ID = b.Table1ID
As you can see, I'm trying to pull all fields from Table1 and a sum of the field "Time" in Table2, where the primary and foreign keys match.
If there isn't a foreign key, I still want the record from Table1 to display, the "Time" field should simply show a 0.
First it sounds like you need to use aggregation since you're trying to sum the times for a given id. Use group by for that and define the fields as needed (vs *).
Second, you need to use an outer join to return those records that don't have matches.
Finally, you can use coalesce to convert null sums to 0:
SELECT a.id,
coalesce(sum(b.Time),0) AS Time
FROM Table1 AS a
LEFT JOIN Table2 AS b ON a.ID = b.Table1ID
GROUP BY a.id
SQL Fiddle Demo
Your query as written would not execute because you are not allowed to do an aggregate function in the select clause if other fields are present that are not in a group by clause. So a couple of options are:
Include every field in Table1 into your select and group by clauses such as:
SELECT a.ID, a.Attribute1, a.Attribute2, a.Attribute3...
, coalesce(sum(b.Time), 0) AS Time
FROM Table1 AS a
LEFT JOIN Table2 AS b
ON a.ID = b.Table1ID
Group by a.ID, a.Attribute1, a.Attribute2, a.Attribute3
Or you can create a sub-query that does the aggregation on on Table2 that is then joined with Table1.
SELECT a.*
, coalesce(b.Time, 0) AS Time
FROM Table1 AS a
LEFT JOIN
(
SELECT Table1ID, SUM(Time) Time
FROM Table2
GROUP BY Table1ID
) AS b
ON a.ID = b.Table1ID
Note that you need a LEFT JOIN, which means that every record in the first table (Table1) is returned whether or not there is a matching record in the second table. If you don't want a null in your results if there isn't a match, then you add the coalesce function to turn any nulls to zeros.
I would like to join two tables.
One table has the following structure:
ID1, ID2, Type, Birth date, sex
where ID2 is unique but the same ID1 may have two different ID2's - so two rows in this table.
What I would like to do is, add these values to another table BUT for ID1's with two ID2's I wouldn't like to have two rows but add another column like type2, birth date2, sex2 and fill these values only for ID1s which are not unique.
I tried something like this:
In a first step I already added all ID1s to the table where type=1. There are several ID1s left, where type=2. Now I would like to add these to the table like this
create table VB_VT_hvgruppe_LV_PRV_VPVT_HILF as
select
a . *,
c.ns5_vptypid as ns5_vptypid2,
c.ns5_gebdat as ns5_gebdat2,
c.ns5_sex as ns5_sex2
from
VB_VT_hvgruppe_LV_PRV_VPVT a
join,
(select
b.ns5_vpvt_ordinal, b.ns5_vptypid, b.ns5_gebdat, b.ns5_sex
from
lfglende.ns5_vpvt1 b
where
b.ns5_vptypid = '2') as c ON a.ns5_vb_ordinal = b.ns5_vpvt_ordinal
where
a.ns5_vb_ordinal = c.ns5_vpvt_ordinal
but this did not work.
Could you help me out?
You have an error in your syntax:
VB_VT_hvgruppe_LV_PRV_VPVT a join, (select
shouldn't have the comma - try this:
create table VB_VT_hvgruppe_LV_PRV_VPVT_HILF as
select
a . *,
c.ns5_vptypid as ns5_vptypid2,
c.ns5_gebdat as ns5_gebdat2,
c.ns5_sex as ns5_sex2
from
VB_VT_hvgruppe_LV_PRV_VPVT a
join
(select
b.ns5_vpvt_ordinal, b.ns5_vptypid, b.ns5_gebdat, b.ns5_sex
from
lfglende.ns5_vpvt1 b
where
b.ns5_vptypid = '2') as c ON a.ns5_vb_ordinal = b.ns5_vpvt_ordinal
where
a.ns5_vb_ordinal = c.ns5_vpvt_ordinal
If you know there are at most two values of ID2 for any particular ID1, then you can join the table to itself, getting the lower value on the "left" and the higher value on the "right".
select T1.ID1, T1.ID2, T1.Type, T1.Birthdate, T1.sex
T2.ID2 as T2ID2, T2.Type as TYPE2, T2.Birthdate as Birthdate2, T2.sex as sex2
from mytable t1
left join mytable t2
on t1.ID1=T2.ID1 and t1.ID2<t2.ID2
If you don't want the nulls in the T2 values, where there's just one record, use COALESCE() on each of those fields.
Here is my sql fiddle.
http://sqlfiddle.com/#!2/7f0780/1/0
I seem to have a problem that when I group two columns to get the max() value it returns the wrong associated data.
You will see the id's are incorrect.
Could someone please help me.
create table table1 (id int,id1 int, id2 int, version int);
insert into table1 values
(1,7,9,1),
(2,7,9,2),
(3,7,9,3),
(4,7,9,4),
(5,9,7,5),
(6,9,7,6);
SELECT max(version),id
FROM table1
group BY
id1,id2
MAX(VERSION) ID
4 1
6 5
Your SQL Query is:
SELECT max(version), id
FROM table1
group BY id1, id2
Note that you are grouping by two columns. But, you are selecting neither of them in the select statement. Instead, you have id. The value of id comes from an arbitrary row, as explained in the MySQL documentation. My advice is to never use this extension, unless you really, really understand what you are doing.
If you want the id associated with the maximum value, you can do it using not exists:
select *
from table1 t
where not exists (select 1
from table1 t1
where t1.id1 = t.id1 and
t1.id2 = t.id2 and
t1.version > t.version
);
That is, select all rows from table1 where the version for the id1/id2 pair has no larger value.
EDIT:
I should add that for performance reasons, an index on table1(id1, id2, version) will help this query a lot.
I am using query like
select * from audittable where a_id IN (1,2,3,4,5,6,7,8);
For each ID its returning 5-6 records. I wanted to get the last but one record for each ID.
Can i do this in one sql statement.
Try this query
SELECT
*
FROM
(SELECT
#rn:=if(#prv=a_id, #rn+1, 1) as rId,
#prv:=a_id as a_id,
---Remaining columns
FROM
audittable
JOIN
(SELECT #rn:=0, #prv:=0) t
WHERE
a_id IN (1,2,3,4,5,6,7,8)
ORDER BY
a_id, <column> desc)tmp --Replace column with the column with which you will determine it is the last record
WHERE
rId=1;
If your database is having DateCreated or any column in which you are saving the DateTime as well like when your data is inserted for a particular row then you may use query like
select at1.* from audittable at1 where
datecreated in( select max(datecreated) from audittable at2
where
at1.id = at2.id
order by datecreated desc
);
You may also use LIMIT function as well.
Hope you understand and works for you.
In SQLite, you have the columns a_id and b. For each a_id you get a set of b's. Let you want
to get the latest/highest (maximum in terms of row_id, date or another naturally increasing index) one of b's
SELECT MAX(b), *
FROM audittable
GROUP BY a_id
Here MAX help to get the maximum b from each group.
Bad news that MySQL doesn't associate MAX b with other *-columns of the table. But it still can be used in case of simple table with a_id and b columns!
I have two tables Board1 and Board2 with the identical structure. They both have a primary index column of id. I have a THIRD table called Table1, which has a non-indexed column board_id, where the same board_id occurs multiple times. board_id always corresponds to an id in Board1. Board2 is currently empty, and I want to add rows from Board1, but only where the same board_id occurs at least six times in Table1. Table1 will be changing periodically, so I'll be needing to do the query in the future, but without doubling id rows which are already in Board2.
So to recap:
There are three tables: Board1, Board2, and Table1. I want to copy rows from Board1 to Board2, but only where the id in the Board1 occurs (at least) six times in Table1 as `board_id'.
I'd appreciate any help!
EDIT: I'm dreadfully sorry, but I realized I made a huge mistake in my question. I've rewritten it to reflect what I actually needed. I'm truly sorry.
You can do it like this
INSERT INTO Table2
SELECT
id,
board_id
FROM (SELECT
b.id,
b.board_id,
bl.Count
FROM board as b
LEFT JOIN (SELECT
board_id,
COUNT(board_id) as `Count`
FROM board
GROUP BY board_id) as bl
on bl.board_id = b.board_id
group by b.id
having bl.Count >= 6) as L
If you need more columns you can select them in inner and outer queries.
Fiddle Demo for Select
Here is what you asked for, with fiddle
INSERT Table2
SELECT
*
FROM
Table1
JOIN
(
SELECT
Board_Id,
count(*) cnt
FROM
Table1
GROUP BY
Board_Id
) BoardIds
ON BoardIds.Board_Id = Table1.Board_Id
WHERE
BoardIds.cnt > 5
AND
NOT EXISTS (SELECT id FROM Table2 WHERE Table2.id = Table1.id)
Try something like the below:
Add your column names where specified (excluding any ID columns), as I'm assuming each row will have a unique ID, so you won't be able to GROUP and COUNT by doing SELECT * FROM Table1
You may need to test / validate this
INSERT INTO Board2 (Your Column Names)
SELECT (Your Column Names)
FROM Board1
WHERE id (IN (SELECT board_id
FROM Table1
GROUP BY (board_id)
HAVING (COUNT(*) >= 6))
AND board_id NOT IN(SELECT DISTINCT board_id FROM Board2)