Compare columns from 2 different tables with only last inserted values in table_2 in SQL Server - sql

If I have two different tables in a SQL Server 2019 database as follows:
Table1
|id | name |
+-----+--------+
| 1 | rose |
| 2 | peter |
| 3 | ann |
| 4 | rose |
| 5 | ann |
Table2
| name2 |
+--------+
|rose |
|ann |
I would like to retrieve only the last tow ids from table1 (which in this case 4 and 5) that match name2 in table2. In other words, match happens only once on the last added names in table1, furthermore, the ids (4, 5) to be inserted in table2.
How to do that using SQL?
Thank you

You can use row_number()
select name,id from
(
select *, row_number() over(partition by t.name order by id desc) as rn
from table1 t join table2 t1 on t.name=t1.name2
)A where rn=1

Your question is vague, so there could be many answers here. My first thought is that you simply want an inner join. This will fetch ONLY the data that both tables share.
SELECT Table1.*
FROM Table1
INNER JOIN Table2 on Table1.name = Table2.name2

You seem to be describing:
select . . . -- whatever columns you want
from (select top (2) t1.*
from table1 t1
order by t1.id desc
) t1 join
table2 t2
on t2.name2 = t1.name;
This doesn't seem particularly useful for the data you have provided, but it does what you describe.
EDIT:
If you want only the most recent rows that match, use row_number():
select . . . -- whatever columns you want
from (select t1.*,
row_number() over (partition by name order by id desc) as seqnum
from table1 t1
) t1 join
table2 t2
on t2.name2 = t1.name and t1.seqnum = 1;

Related

sql synthesis query

good morning.
Can anyone help me with the query, summarizing the total code in each table (Table 1 + table 2) as in table 3, thanks a lot.
Sorry, my english is not good
SELECT A.code, count(A.Date) as bang_1 , B.bang_2
FROM table_1 A
LEFT JOIN (SELECT code,count(*) as bang_2
FROM table_2
GROUP BY code) B ON A.code = B.code
GROUP BY A.code, B.bang_2
I think I understand what you are trying to do:
select codes.codename, t1.c as Table1, t2.c as Table2
from (
select codename from table1
union select codename from table2
) codes
left join (select codename, count(*) c from table1 group by codename) t1 on codes.codename = t1.codename
left join (select codename, count(*) c from table2 group by codename) t2 on codes.codename = t2.codename
group by codes.codename
order by codes.codename;
Example: https://dbfiddle.uk/?rdbms=mysql_5.5&fiddle=74d3bf30af406a2cdf65185a5fdcc564
Explanation
There are 2 subqueries t1 and t2. Both of them take a count of each code from each of the tables. The codes subquery combines codenames from both tables in case one table had more codes than the other.
Then, using the combined codenames connect with t1 and t2 and collect its respective counts.
Result
codename | Table1 | Table2
:------- | -----: | -----:
code1 | 2 | 1
code2 | 2 | 3
code3 | 1 | 1

How can I join two tables with a many-to-one relationship ordered by date?

I need to join two tables and get the most recent record only. Here is the basic form:
table1.id | table1.region | table1.important_col1
1 | NORTH AMERICA | abc
2 | CHINA | def
2 | NORTH AMERICA | hij
table2.id | table2.region | table2.transaction_date | table2.important_col2
1 | NORTH AMERICA | 2/13/2019 | xyz
1 | NORTH AMERICA | 1/13/2019 | zzz
1 | NORTH AMERICA | 12/13/2018 | xxx
desired result:
1 | NORTH AMERICA | 2/13/2019 | abc | xyz
I wanted to use this answer but it seems like I can't use it if I need to group by and then order by descending date. I will need information in multiple columns on the right hand side, but do not want duplicate rows on the left hand side.
The right hand side may have up to 100s of records per id, but I just need something that works for now. Thanks in advance.
edit: I also need to filter the right hand side on other criteria so a simple MAX(table2.transaction_date) won't work.
You can filter your table using internal window function, I used LAG for this example, but you can use ROW_NUMBER and filter several records. Using sliding windows does not change the number of records or counted as SQL aggregation, i.e. you filter using where rather than with having.
SELECT
t1.id
,t2.transaction_date
,t1.region
,t1.col1
,t2.important_col2
FROM table1 AS t1
OUTER APPLY (
SELECT
id
,transaction_date
,LAG(transaction_date,1) over (partition by id order by transaction_date desc) as prev_td
,important_col2
FROM table2
-- WHERE filter_by_col=1 -- additonal "right side" filtering
) as t2
where t1.id = t2.id
and t2.prev_td is null
Output:
1 2019-02-13 00:00:00.000 NORTH AMERICA abc xyz
I used this to test the above query:
create table table1
(id int,
region varchar(30),
col1 varchar(100));
insert into table1
values (1 ,'NORTH AMERICA' ,'abc'),
(2,'CHINA','def'),
(2,'NORTH AMERICA','hij');
create table table2
(id int,
region varchar(30),
transaction_date datetime,
important_col2 varchar(100))
insert into table2
values
(1 ,'NORTH AMERICA',convert(datetime, '02/13/19', 1),'xyz'),
(1 ,'NORTH AMERICA',convert(datetime, '01/13/19',1),'zzz'),
(1 ,'NORTH AMERICA',convert(datetime, '12/13/18',1),'xxx')
Try in this way:
select table11.id, table1.region, max(table2.transaction_date) transaction_date
from table1
inner join table2
on table1.id = table2.id
group by table1.id, table1.region
If there are more columns in table2 (other than transaction date) that you want to display as well, then aggregation alone cannot solve your question.
In MySQL 8.0 you can use window function ROW_NUMBER() to identify the most recent transaction record, as follows :
SELECT x.*
FROM (
SELECT
t1.*,
t2.*,
ROW_NUMBER() OVER(PARTITION BY t2.region ORDER BY t2.transaction_date DESC) rn
FROM table1 t1
INNER JOIN table2 t2 ON t1.region = t2.region
) x
WHERE x.rn = 1
In earlier versions of MySQL, one solution is to add a NOT EXISTS with a correlated subquery that ensures that we are joining with the most recent transaction for the current region :
SELECT t1.*, t2.*
FROM table1 t1
INNER JOIN table2 t2
ON t1.region = t2.region
AND NOT EXISTS (
SELECT 1
FROM table2
WHERE region = t2.region AND transaction_date > t2.transaction_date
)

Oracle Efficiently joining tables with subquery in FROM

Table 1:
| account_no | **other columns**...
+------------+-----------------------
| 1 |
| 2 |
| 3 |
| 4 |
Table 2:
| account_no | TX_No | Balance | History |
+------------+-------+---------+------------+
| 1 | 123 | 123 | 12.01.2011 |
| 1 | 234 | 2312 | 01.03.2011 |
| 3 | 232 | 212 | 19.02.2011 |
| 4 | 117 | 234 | 24.01.2011 |
I have multiple join query, one of the tables(Table 2) inside a query is problematic as it is a view which computes many other things, that is why each query to that table is costly. From Table 2, for each account_no in Table 1 I need the whole row with the greatest TX_NO, this is how I do it:
SELECT * FROM TABLE1 A LEFT JOIN
( SELECT
X.ACCOUNT_NO,
HISTORY,
X.BALANCE
FROM TABLE2 X INNER JOIN
(SELECT
ACCOUNT_NO,
MAX(TX_NO) AS TX_NO
FROM TABLE2
GROUP BY ACCOUNT_NO) Y ON X.ACCOUNT_NO = Y.ACCOUNT_NO) B
ON B.ACCOUNT_NO = A.ACCOUNT_NO
As I understand at first it will make the inner join for all the rows in Table2 and after that left join needed account_no's with Table1 which is what I would like to avoid.
My question: Is there a way to find the max(TX_NO) for only those accounts that are in Table1 instead of going through all? I think it will help to increase the speed of the query.
I think you are on the right track, but I don't think that you need to, and would not myself, nest the subqueries the way you have done. Instead, if you want to get each record from table 1 and the matching max record from table 2, you can try the following:
SELECT * FROM TABLE1 t1
LEFT JOIN
(
SELECT t.*,
ROW_NUMBER() OVER (PARTITION BY account_no ORDER BY TX_No DESC) rn
FROM TABLE2 t
) t2
ON t1.account_no = t2.account_no AND
t2.rn = 1
If you want to continue with your original approach, this is how I would do it:
SELECT *
FROM TABLE1 t1
LEFT JOIN TABLE2 t2
ON t1.account_no = t2.account_no
INNER JOIN
(
SELECT account_no, MAX(TX_No) AS max_tx_no
FROM TABLE2
GROUP BY account_no
) t3
ON t2.account_no = t3.account_no AND
t2.TX_No = t3.max_tx_no
Instead of using a window function to find the greatest record per account in TABLE2, we use a second join to a subquery instead. I would expect the window function approach to perform better than this double join approach, and once you get used to it can even easier to read.
If table1 is comparatiely less expensive then you could think of doing a left outer join first which would considerable decrease the resultset and from that pick the latest transaction id records alone
select <required columns> from
(
select f.<required_columns),row_number() over (partition by account_no order by tx_id desc ) as rn
from
(
a.*,b.tx_id,b.balance,b.History
from table1 a left outer join table2 b
on a.account_no=b.account_no
)f
)g where g.rn=1

Distinct Value of a column in sql server 2008

Hello all I have made a query using left outer joins which result in some what like the table below:
| 00-00-00-00-00 | 1 | a.txt |
| 00-00-00-00-00 | 2 | b.txt |
| 00-00-00-00-00 | 1 | c.txt |
| 11-11-11-11-11 | 2 | d.txt |
What I want is Distict value of the MAC Column below is the SQL Fiddle to understand better.
SQLFIDDLE
Thanks
EDIT
The purpose is that 2 and 3 are useless or redundant data where as 1 and 4 are useful means the 1 and 4 show the current file on the MACs
Output:
| 00-00-00-00-00 | 1 | a.txt |
| 11-11-11-11-11 | 2 | d.txt |
Is not possible to answer exactly what you ask. However, usually folk that express the question you ask really mean to ask something like 'I want all the columns for a sample of rows containing only distinct MacAddress values'. This question has many answers, as the result is non-deterministic. A trivial solution is to pick the first (for whatever definition of 'first') row for each MacAddress:
with cte as (
select row_number() over (partition by MacAddress order by CounterNo) as rn, *
from Heartbeats
)
select * from cte where rn = 1;
If you want to get only the distinct macaddresses, you can do:
SELECT DISTINCT macaddress FROM heartbeats
If you want all the columns alongside the distinct macaddress, you need to create a rule to get them. The query below gives you the ones with the highest id for each macaddress:
SELECT t1.*
FROM heartbeats t1
LEFT JOIN heartbeats t2
ON (t1.macaddress = t2.macaddress AND t1.id < t2.id)
WHERE t2.id IS NULL
sqlfiddle demo
EDIT:
Since in original query the code used doesnt have ID column the above query was refined as:
with cte as (
select ROW_NUMBER() OVER(ORDER BY (Select 0)) AS ID,* from heartBeats
)
SELECT t1.*
FROM cte t1
LEFT JOIN cte t2
ON (t1.macaddress = t2.macaddress AND t1.id < t2.id)
WHERE t2.id IS NULL
SQL Fiddle
SELECT hb1.* FROM [heartbeats] as hb1
LEFT OUTER JOIN [heartbeats] as hb2
ON (hb1.macaddress = hb2.macaddress AND hb1.id > hb2.id)
WHERE hb2.id IS NULL;
You have to neglect the file name. See http://sqlfiddle.com/#!3/a75e47/13

PostgreSQL LEFT OUTER JOIN query syntax

Lets say I have a table1:
id name
-------------
1 "one"
2 "two"
3 "three"
And a table2 with a foreign key to the first:
id tbl1_fk option value
-------------------------------
1 1 1 1
2 2 1 1
3 1 2 1
4 3 2 1
Now I want to have as a query result:
table1.id | table1.name | option | value
-------------------------------------
1 "one" 1 1
2 "two" 1 1
3 "three"
1 "one" 2 1
2 "two"
3 "three" 2 1
How do I achieve that?
I already tried:
SELECT
table1.id,
table1.name,
table2.option,
table2.value
FROM table1 AS table1
LEFT outer JOIN table2 AS table2 ON table1.id = table2.tbl1fk
but the result seems to omit the null vales:
1 "one" 1 1
2 "two" 1 1
1 "one" 2 1
3 "three" 2 1
SOLVED: thanks to Mahmoud Gamal: (plus the GROUP BY)
Solved with this query
SELECT
t1.id,
t1.name,
t2.option,
t2.value
FROM
(
SELECT t1.id, t1.name, t2.option
FROM table1 AS t1
CROSS JOIN table2 AS t2
) AS t1
LEFT JOIN table2 AS t2 ON t1.id = t2.tbl1fk
AND t1.option = t2.option
group by t1.id, t1.name, t2.option, t2.value
ORDER BY t1.id, t1.name
You have to use CROSS JOIN to get every possible combination of name from the first table with the option from the second table. Then LEFT JOIN these combination with the second table. Something like:
SELECT
t1.id,
t1.name,
t2.option,
t2.value
FROM
(
SELECT t1.id, t1.name, t2.option
FROM table1 AS t1
CROSS JOIN table2 AS t2
) AS t1
LEFT JOIN table2 AS t2 ON t1.id = t2.tbl1_fk
AND t1.option = t2.option
SQL Fiddle Demo
Simple version: option = group
It's not specified in the Q, but it seems like option is supposed to define a group somehow. In this case, the query can simply be:
SELECT t1.id, t1.name, t2.option, t2.value
FROM (SELECT generate_series(1, max(option)) AS option FROM table2) o
CROSS JOIN table1 t1
LEFT JOIN table2 t2 ON t2.option = o.option AND t2.tbl1_fk = t1.id
ORDER BY o.option, t1.id;
Or, if options are not numbered in sequence, starting with 1:
...
FROM (SELECT DISTINCT option FROM table2) o
...
Returns:
id | name | option | value
----+-------+--------+-------
1 | one | 1 | 1
2 | two | 1 | 1
3 | three | |
1 | one | 2 | 1
2 | two | |
3 | three | 2 | 1
Faster and cleaner, avoiding the big CROSS JOIN and the big GROUP BY.
You get distinct rows with a group number (grp) per set.
Requires Postgres 8.4+.
More complex: group indicated by sequence of rows
WITH t2 AS (
SELECT *, count(step OR NULL) OVER (ORDER BY id) AS grp
FROM (
SELECT *, lag(tbl1_fk, 1, 2147483647) OVER (ORDER BY id) >= tbl1_fk AS step
FROM table2
) x
)
SELECT g.grp, t1.id, t1.name, t2.option, t2.value
FROM (SELECT generate_series(1, max(grp)) AS grp FROM t2) g
CROSS JOIN table1 t1
LEFT JOIN t2 ON t2.grp = g.grp AND t2.tbl1_fk = t1.id
ORDER BY g.grp, t1.id;
Result:
grp | id | name | option | value
-----+----+-------+--------+-------
1 | 1 | one | 1 | 1
1 | 2 | two | 1 | 1
1 | 3 | three | |
2 | 1 | one | 2 | 1
2 | 2 | two | |
2 | 3 | three | 2 | 1
-> SQLfiddle for both.
How?
Explaining the complex version ...
Every set is started with a tbl1_fk <= the last one. I check for this with the window function lag(). To cover the corner case of the first row (no preceding row) I provide the biggest possible integer 2147483647 the default for lag().
With count() as aggregate window function I add the running count to each row, effectively forming the group number grp.
I could get a single instance for every group with:
(SELECT DISTINCT grp FROM t2) g
But it's faster to just get the maximum and employ the nifty generate_series() for the reduced CROSS JOIN.
This CROSS JOIN produces exactly the rows we need without any surplus. Avoids the need for a later GROUP BY.
LEFT JOIN t2 to that, using grp in addition to tbl1_fk to make it distinct.
Sort any way you like - which is possible now with a group number.
try this
SELECT
table1.id, table1.name, table2.option, table2.value FROM table1 AS table11
JOIN table2 AS table2 ON table1.id = table2.tbl1_fk
This is enough:
select * from table1 left join table2 on table1.id=table2.tbl1_fk ;