I have a table with the following structure:
ID KEY VALUE SEQ
1 Amount 5 2
1 Amount 4 1
1 Type T1 2
1 Type T1 1
2 Amount 10 2
2 Amount 5 1
2 Type T2 2
2 Type T2 1
I would like to create a query to get this:
ID Amount Type
1 5 T1
2 10 T2
As you can see there could be multiple combinations of (ID, Key) but (ID, Key, Seq) is unique.
SELECT T.ID,
T1.VALUE as Amount,
T2.VALUE as Type
FROM
(SELECT ID, MAX(SEQ) as MAXSEQ FROM TABLE GROUP BY ID) as T
JOIN
TABLE as T1
ON T1.ID = T.ID
AND T1.KEY = 'Amount'
AND T1.SEQ = MAXSEQ
JOIN
TABLE as T2
ON T2.ID = T.ID
AND T2.KEY = 'Type'
AND T2.SEQ = MAXSEQ
But I am getting results that I wasn't expecting
ID Amount Type
1 5 T1
1 4 T1
1 10 T1
1 5 T1
2 10 T2
2 5 T2
2 4 T2
2 5 T2
I already read this post but it doesn't apply to my case although it helps here
Any idea on who to fix this?
SELECT
id, amount, type
FROM TABLE1
natural join (SELECT ID, MAX(SEQ) as SEQ FROM TABLE1 GROUP BY ID)
pivot (
max(VALUE) for key in ('Amount' as amount, 'Type' as type)
)
fiddle
Something you are missing from the linked question is the DISTINCT keyword.
See also the explanation below the query posted by ypercube. In fact, you are getting duplicates because you are joining a table on itself. Thus, rows will be mirrorred.
Your subquery should be:
(SELECT DISTINCT ID, MAX(SEQ) as MAXSEQ FROM TABLE GROUP BY ID) as T
I realized that there was another column, that I didn't put in the question (to avoid giving out real data)which was affecting the results. The unique constraint was (ID, Key, Seq, Time) instead of (ID, Key, Seq). As #Andrew Mentioned the query is returning the correct results.
Here goes the query again
SELECT T.ID,
T1.VALUE as Amount,
T2.VALUE as Type
FROM
(SELECT ID, MAX(SEQ) as MAXSEQ FROM TABLE GROUP BY ID) as T
JOIN
TABLE as T1
ON T1.ID = T.ID
AND T1.KEY = 'Amount'
AND T1.SEQ = MAXSEQ
JOIN
TABLE as T2
ON T2.ID = T.ID
AND T2.KEY = 'Type'
AND T2.SEQ = MAXSEQ
Thanks Andrew for the clarification and the sqlfiddle. I apologize if I wasted anyone's time.
Related
I need to create a table keyed by an ID where the values of one of the columns in the new table are the earliest values entered into the column of another table where the rows share the same ID and have a specific type label.
For example, say I want the Name and first Value entered for each fruit with an entry type A:
These are the tables I have:
TABLE1
Key
ID
Name
1
1
Cherry
2
2
Grape
TABLE2
Key
ID
Value
EntryNum
EntryType
1
1
21
1
A
2
1
32
2
B
3
1
4
3
B
4
1
15
4
A
5
2
3
1
B
6
2
8
2
A
7
2
16
3
B
And this is the result that I want:
TABLE3
ID
Name
EarliestEntry
1
Cherry
21
2
Grape
8
I've attempted the following query but it just returns the same value for all EarliestEntry:
SELECT TABLE1.ID, TABLE2.Name,
(SELECT Value FROM (SELECT ROW_NUMBER() OVER (ORDER BY TABLE2.EntryNum)
as row_num, Value FROM TABLE2
WHERE TABLE2.ID = TABLE1.ID AND TABLE2.EntryType = 'A')
AS sub
WHERE row_num = 1) AS EarliestEntry
INTO TABLE3
FROM TABLE2
INNER JOIN TABLE1 ON TABLE1.ID = TABLE2.ID
GROUP BY TABLE1.ID, TABLE2.Type, TABLE2.EntryNum
I would greatly appreciate help on this. Thank you
If you wanted to use the ROW_NUMBER function then you would need to put that on TABLE1 and add a partition by like so:
WITH rn AS(
SELECT a.Key, ROW_NUMBER() OVER(PARTITION BY a.ID ORDER BY a.EntryNum) AS rn
FROM TABLE2 AS a
)
SELECT b.Name, a.Value AS EarliestValue
FROM TABLE2 AS a
INNER JOIN TABLE1 AS b ON b.ID = a.ID
INNER JOIN rn AS rn ON rn.key = a.key
WHERE rn.rn = 1
In your example you skipped the PARTITION BY clause so you just get a number for all values in TABLE2. Instead of a number per ID in ascending order for Value.
Based on your description of the three tables TABLE1, TABLE2 and TABLE3.
I modified a little bit your script. Thank of Dale K remark, I explain in some words the solution : the field TABLE2.Name shown in the first select was wrong, because [name] belongs to TABLE1, so the right syntax for this is TABLE1.name. And in the GROUP BY clause the field TABLE2.Type might be replaced by TABLE1.name to repect aggregation criteria. So the script becomes :
SELECT DISTINCT table1.id, table1.name,
(SELECT Value FROM (SELECT ROW_NUMBER() OVER (ORDER BY table2.EntryNum)
as row_num, Value FROM table2
WHERE table2.id = table1.id AND table2.EntryType = 'A')
AS sub
WHERE row_num = 1) AS EarliestEntry
INTO table3
FROM table2
INNER JOIN table1 ON table1.id = table2.id
GROUP BY table1.id, table1.name, table2.entrynum;
Here, you can verify the output with fiddle
You are hugely over-complicating this.
Just partition Table2 and take a row-number, then join that to Table1 and filter on only row-number 1
SELECT
t1.Id,
t1.Name,
EarliestEntry = t2.Value
FROM Table1 t1
JOIN (
SELECT *,
rn = ROW_NUMBER() OVER (PARTITION BY t2.ID ORDER BY t2.EntryNum)
FROM Table2 t2
WHERE t2.EntryType = 'A'
) t2 ON t2.ID = t1.ID AND t2.rn = 1;
db<>fiddle
I have a 3 tables, one with these two columns
table1:
id, name
0 foo
1 etc
2 example
table2:
id table1_id
0 1
1 0
2 2
table3:
id table2_id
0 1
1 0
2 0
Which query can I find all 'name's from table1 where ALL ids in table2 have a count of atleast n in table3? i.e if n was 1 it should return foo and etc
EDIT:
Explained poorly, I'm trying to get the name of every record in table1 where ALL corresponding records in table2 (i.e records where the column table1_ID is equal to each id within table1. In my example tables, each ID has one) have a count in table3 of atleast n.
If n was 1, as the table2_id 0 appears twice in records 1 and 2, its 'parent' would be returned. It corresponds to the table 1 record 1, so the name of the record with table1 id: 1 should be returned, which is etc. Example also as it has a count of 1 in the bottom column, however foo does not appear so it shouldnt.
Expected result:
name
foo
etc
You can do this using a subquery in the where clause:
select t1.*
from table1 t1
where (select count(t3.id)
from table2 t2 left join
table3 t3
on t3.table2_id = t2.id
where t2.table1_id = t1.id
group by t2.id
order by count(*) asc -- to get the minimum
limit 1
) >= ? -- value you care about
I suspect that this might have the best performance with appropriate indexes: table2(table1_id, id) and table3(table2_id).
If I have understood the question - if a check on table3.table2_id is greater than 0, the answer would be 'etc' ?
Code below
select t1.name
from
(
select 0 as id, 1 as table2_id
union select 1, 0
union select 2 , 0
) t3
inner join
(
select 0 as id , 1 as table_id
union select 1, 0
union select 2, 2
) t2 on t2.table_id = t3.table2_id
inner join
(
select 0 as id, 'foo' as name
union select 1 , 'etc'
union select 2 , 'example'
) t1 on t1.id = t2.table_id
where t3.table2_id > 0
select table1.name
FROM table1
INNER JOIN table2 ON table1.id=table2.table1_id
INNER JOIN table3 ON table2.id=table3.table2_id
GROUP BY table1.name
HAVING count(*) >= 1
replace the last 1 with whatever n you desire
Here's the sql fiddle if you want to play around with it: http://sqlfiddle.com/#!7/14217/4
Use an INNER join of table1 to table2 and then a LEFT join to table3 and count the corresponding ids of table3.
Then by a 2nd level of aggregation return only the rows of table1 where all the counters are at least 1:
SELECT id, name
FROM (
SELECT t1.id, t1.name, COUNT(t3.id) counter
FROM table1 t1
INNER JOIN table2 t2 ON t2.table1_id = t1.id
LEFT JOIN table3 t3 ON t3.table2_id = t2.id
GROUP BY t1.id, t1.name, t2.id
)
GROUP BY id, name
HAVING MIN(counter) >= 1 -- change to the number that you want
See the demo.
Results:
id
name
0
foo
1
etc
I have two tables (SQL-server):
t1 (parent)
===========
id
1
2
t2 (child)
=======================
parent_id record_number
1 1
2 1
2 2
Is it possible in one SQL statement to insert new records into t2 for each of parent id, so the result will be:
t2:
=======================
parent_id record_number
1 1
1 2
2 1
2 2
2 3
Thank you!
I think what the OP is after is:
INSERT INTO T2
SELECT T1.id
MAX(T2.ID) + 1
FROM T1
JOIN T2 ON T1.ID = T2.parent_id
GROUP BY T1.id;
Something like this should get what you want.
Note I added it with a LEFT JOIN so you could use ISNULL in case the parent record isn't in t2 yet.
INSERT INTO t2 (parent_id, record_number)
SELECT
A.id,
ISNULL(MAX(B.record_number), 0) + 1
FROM t1 A
LEFT JOIN t2 B
ON A.id = B.parent_id
GROUP BY
A.id
As an aside, generating a record number could be done effectively using the ROW_NUMBER() function. Here is an example. Perhaps you have specific reasons why it needs to be persisted to the table, but if not this could be useful as well to calculate it on the fly.
SELECT
*,
ROW_NUMBER() OVER (PARTITION BY parent_id ORDER BY id)
FROM t2
ORDER BY parent_id, record_number
Note it assumes you have an "id" column in t2, or something else to order the records by
Is this what you want?
insert into t2 (parent_id, record_number)
select id, id
from t1;
If so, I prefer using NULL to represent that that something has no parent:
insert into t2 (parent_id, record_number)
select null, id
from t1;
This is a more accurate representation of the data. Something is not its own parent.
I have a one-to-many relation.
Table 1: C_Id, Name
Table 2: Id, Name
I want to select only one row from table 2 for each Id in table C_Id.
Example:
Table 1
1 First Row
2 Second Row
Table 2
1 First Row 1
1 First Row 2
2 Second Row 1
2 Second Row 2
My query result is:
1 First Row 1 (First row for ID 1)
2 Second Row 1 (First row for ID 2)
What query do I need to get this?
SELECT t1.C_Id, MIN(t2.Name) -- or MAX, depending on your needs
FROM t1 INNER JOIN t2 ON (t1.C_Id = t2.Id)
GROUP BY t1.C_Id
select * from tab1, tab2 where tab1.c_id=tab2.id and rownum=1;
--use order by if you want to sort ace or desc.
If you can rely on the ID of table 2 then this is quite simple:
select t1.c_id
, t1.name
, t2.id as t2_id
from t1
join t2 on t1.name = t2.name
where t2.id = 1;
However I suspect your actual case is more complicated. But easily solvable with the ROW_NUMBER() analytic function:
select c_id
, name
, t2_id
from (
select t1.c_id
, t1.name
, t2.id as t2_id
, row_number() over ( partition by t1.name
order by t2.id asc ) rn
from t1
join t2 on t1.name = t2.name
)
where rn = 1;
I suppose this is not so hard but I can not get it.
For example I have table T1:
ID
-----
1000
1001
And I have table T2:
ID GROUP DATE
--------------------------
1000 ADSL 2.2.2012
1000 null 3.2.2012
1000 NOC 4.2.2012
1001 NOC 5.2.2012
1001 null 6.2.2012
1001 TV 7.2.2012
I want to select from T1 only the row that has as GROUP value NOC from T2 but only if NOC group is for the minimum DATE value in T2.
So my result in this case would be only 1001 because for its minimum DATE 5.2.2012 Group is NOC!
I do not want any joins and I can not use default values for IDs (where id=1000 or id=1001) because this is just example of some big table.
Important also is that I can not use t1.id = t2.id because in some application where I am using this I can not write the whole SQL expression but only partial. I can only use id.
I tried something like:
select id
from t1
where
id in (select id from t2
where group = 'NOC'
and date in (select min(date) from t2
where id in (select id from t1)
)
)
But this does not work.
I know it seems little confusing but I really can't use where t1.id = t2.id
Thanks
If T2.ID is a foreign key referencing T1.ID, you don't really need the T1 table, because all the IDs could be obtained from T2 only:
SELECT o.ID
FROM T2 AS o
WHERE EXISTS (
SELECT MIN(i.DATE)
FROM T2 AS i
WHERE i.ID = o.ID
HAVING MIN(i.DATE) = o.DATE
)
WHERE o."GROUP" = 'NOC'
But if you insist on involving T1, you just need to modify the above like this:
SELECT *
FROM T1
WHERE ID IN (
SELECT o.ID
FROM T2 AS o
WHERE o."GROUP" = 'NOC'
AND EXISTS (
SELECT MIN(i.DATE)
FROM T2 AS i
WHERE i.ID = o.ID
HAVING MIN(i.DATE) = o.DATE
)
)
Can you do this in multiple steps?
First of all, to get the minimum date per id, you would need:
select id, peoplegroup, min(date)
from t2
group by id
That will give you
1000 ADSL 2.2.2012
1001 NOC 5.2.2012
Call this table t3.
Then do
select id
from t3
where id in (
select id from t1
)
Try this:
select id from t1 where id in
(select id from t2 where group = 'NOC' and date =
(select min(date) from t2 where id = t1.id))