Get the information from one column into another SQL - sql

can anyone help me to solve this problem?
I have two tables I have to fetch all customers who have an unexpired transaction. However, this condition must be true for both tables. For this reason I made a join between the two tables to verify that both dates are not expired,if both exist otherwise if the second is null only the first.Finally I should merge the information I have horizontally into a single column.
For example in this case i have this result for the join:
I would like to union the information of the last three columns in the first three so as to have a single block.
id_client,id_tran,expiry with id_client 1,2,3,5,6 all togheter.
create table tab1 (
id_client int,
id_trans int,
expiry date)
create table tab2 (
id_client int,
id_trans int,
expiry date)
insert into tab1 values (1,101,'02-03-2020')
insert into tab1 values (1,102,'22-07-2022')
insert into tab1 values (2,201,'23-05-2023')
insert into tab1 values (3,301,'02-03-2022')
insert into tab1 values (3,302,'12-10-2024')
insert into tab2 values (4,101,'12-03-2023')
insert into tab2 values (5,102,'21-08-2024')
insert into tab2 values (6,201,'21-08-2024')
SELECT * from tab1 a
left join tab2 b
on a.id_trans = b.id_trans
where a.expiry > getdate()
AND coalesce(B.expiry,'2900-12-31') > getdate()

Here are 2 queries, one of which will return all records when a client has more than 1 unexpired transaction per table, and the other which uses max to return the latest one. (We could equally user min to get the oldest one or string_agg to list them all.)
with clients as
(select id_client from tab1
union select id_client from tab2)
select
c.id_client,
t1.id_trans t1,
t1.expiry t1_expiry,
t2.id_trans t2,
t2.expiry t2_expiry
from clients c
left join tab1 t1
on c.id_client = t1.id_client
left join tab2 t2
on c.id_client = t2.id_client
where coalesce(t1.expiry,'2900-12-31') > getdate()
and coalesce(t2.expiry,'2900-12-31') > getdate()
order by c.id_client;
GO
id_client | t1 | t1_expiry | t2 | t2_expiry
--------: | ---: | :--------- | ---: | :---------
1 | 102 | 2022-07-22 | 101 | 2022-09-12
2 | 201 | 2023-05-23 | null | null
3 | 302 | 2024-10-12 | null | null
4 | null | null | 101 | 2023-03-12
5 | null | null | 102 | 2024-08-21
6 | null | null | 201 | 2024-08-21
6 | null | null | 101 | 2022-09-12
with clients as
(select id_client from tab1
union select id_client from tab2)
select
c.id_client,
max(t1.id_trans) t1,
max(t1.expiry) t1_expiry,
max(t2.id_trans) t2,
max(t2.expiry) t2_expiry
from clients c
left join tab1 t1
on c.id_client = t1.id_client
left join tab2 t2
on c.id_client = t2.id_client
where coalesce(t1.expiry,'2900-12-31') > getdate()
and coalesce(t2.expiry,'2900-12-31') > getdate()
group by c.id_client
order by c.id_client;
GO
id_client | t1 | t1_expiry | t2 | t2_expiry
--------: | ---: | :--------- | ---: | :---------
1 | 102 | 2022-07-22 | 101 | 2022-09-12
2 | 201 | 2023-05-23 | null | null
3 | 302 | 2024-10-12 | null | null
4 | null | null | 101 | 2023-03-12
5 | null | null | 102 | 2024-08-21
6 | null | null | 201 | 2024-08-21
db<>fiddle here

Related

Insert into table with multiple select including select distinct from two tables

I have two SQL Server tables (simplified) below:
Table1
| productname | certno | expdate | company
+-------------+--------+------------+---------
| abc | 123 | 2020-01-01 | ABC
| def | 123 | 2020-01-01 | ABC
| qwe | 456 | 2020-01-02 | ABC
| asd | 999 | 2020-02-02 | DEF
| .. | .. | .. | ..
Table2
| companyid | company
+-----------+---------
| 1 | ABC
| 2 | DEF
| .. | ..
And I need to insert data from these 2 tables into another table for distinct certno, Table3 should be like this:
| certid | companyid | certno | expdate | createddate | is_null
+--------+-----------+--------+------------+-------------+---------
| 1 | 1 | 123 | 2020-01-01 | 2020-09-12 | 1
| 2 | 1 | 456 | 2020-01-02 | 2020-09-12 | 1
| 3 | 2 | 999 | 2020-02-02 | 2020-09-12 | 1
| .. | .. | .. | .. | .. | ..
I'm currently using this query, I need to create a script so that next batch of update is just in one query.
insert into Table3 (certno, expdate, is_null)
select distinct certno, expdate
from Table1
where company like '%ABC%';
--then I will update Table3 to complete it
update Table3
set companyid = '1',
createddate = getdate(),
is_null = '1'
where is_null is null; --edited**
--then repeat the above until I have all companies registered as companyid in Table3
But this will be a hassle when it involved more companies and lots of data and I was thinking of using multiple select including select distinct but I'm stuck right here:
insert into Table3 (companyid, certno, expdate, createddate)
select
(select companyid from Table2
where company in (select company from Table1)
),
(select distinct certno),
expdate,
getdate()
from Table1;
I did not run the script to see whether it works or not because I don't want to mess up the Table3 data and I feel that the script is not going to work.
Is there any way to just run the query in one script?
I think you want insert . . . select with join:
insert into Table3 (companyid, certno, expdate, createddate, is_null)
select distinct t2.companyid, t1.certno, t1.expdate, getdate(), 1
from Table1 t1 join
Table2 t2
on t1.company = t2.company
where t1.company like '%ABC%';
You can remove the where clause to insert all companies in one insert.
Looks like you just need to expand the columns in the SELECT:
INSERT INTO dbo.Table3 (certno, expdate, companyid, createdate)
SELECT DISTINCT
certno,
expdate,
company,
GETDATE()
FROM dbo.Table1;

Gather the max of a set of data by 2 columns in SQL?

I am trying to get the latest of a set of columns by a PersonID out of a set of YearIDs.
If I have a table like this:
| DataID | PersonID | YearID | Data1A | Data1B | Data2A | Data2B |
|--------|----------|--------|--------|--------|--------|--------|
| 1 | 888 | A100 | d | 0.00 | a | 1.00 |
| 2 | 888 | A101 | NULL | NULL | b | 2.00 |
| 3 | 888 | A102 | c | 3.00 | NULL | NULL |
| 4 | 333 | A100 | a | 3.40 | e | 4.00 |
| 5 | 333 | A101 | d | 0.00 | NULL | NULL |
| 6 | 333 | A102 | NULL | NULL | NULL | NULL |
How do I get the latest of column sets Data1A, Data1B and Data2A, Data2B sorted by YearID per PersonID?
This is given that Data1A and Data1B are related and Data2A and Data2B are related and can not be separated, and most recent year is A102. DataID is just an incremental PK column.
My resulting table should look like this, with Year being removed as it's no longer necessary. It should ignore NULLs but not 0's:
| DataID | PersonID | Data1A | Data1B | Data2A | Data2B |
|--------|----------|--------|--------|--------|--------|
| 1 | 888 | c | 3.00 | b | 2.00 |
| 2 | 333 | d | 0.00 | e | 4.00 |
This is what I have so far, but I don't know how to take into account the fact that I want the 'max'/latest of a set of Years by PersonID. Right now it gets the max of each column but I want the most recent valid data by latest year, and it also has Data1 and Data2 not being related at all but I need them to be.
SELECT DISTINCT
T1.SID,
GroupedT1.Data1,
GroupedT1.Data2,
FROM #Table1 T1
INNER JOIN
(SELECT SID,
MAX(Data1) AS Data1,
MAX(Data2) AS Data2,
FROM #Table1
GROUP BY PersonID) GroupedT1
ON T1.PersonID = GroupedT1.PersonID
Editing thanks to Gordon for the previous answer, this is how I tried to fix my new problem:
With this solution I'm trying to get the latest for Data1 and Data2, ignoring as many NULL columns as there is, and picking data from any YearID as long as it's the latest. So if in the year A102, Data1A is NULL then it should pick year A101's Data1A, and if Data2A is null for many years, it should pick the latest (in this case, year A100). At the moment it's close but it only picks by row, and needs to pick by year and with any number of NULL data.
select t1.PersonID, t1.Data1A, t1.Data1B, t1.Data2A, t1.Data2B
from (select t1.*,
row_number() over (partition by SID order by
(case when Data1A is not null then 1 else 2 end),
(case when Data2A is not null then 1 else 2 end),
YearID desc) as seqnum
from #Table1 t1
) t1
where seqnum = 1
This answers the original question.
I think you want a simple filtering before applying logic such as row_number():
select t1.*
from (select t1.*,
row_number() over (partition by personid order by yearid desc) as seqnum
from #table1 t1
where data1 is not null and data2 is not null
) t1
where seqnum = 1;
EDIT:
To answer the revised question, you need to handle each columns separately. You can do this using outer apply:
select p.personid, d1.data1, d2.data2, . . .
from (select distinct personid from #table1) p outer apply
(select top (1) t1.data1
from #table1 t1
where t1.personid = p.personid and t1.data1 is not null
order by t1.yearid desc
) d1 outer apply
(select top (1) t1.data2
from #table1 t1
where t1.personid = p.personid and t1.data2 is not null
order by t1.yearid desc
) d2 . . .
You can use the not exists keywork
SELECT DataID, PersonID, Data1, Data2
FROM #Table1 T1
where not exists(select 1 from #Table1 T2
where T1.DataID = T2.DataID and T2.YearID > T1.YearID)

Select max value record from one-to-many join

I want to join two tables, but the second table contains multiple rows of parameters on which I wish to build by join.
TABLE1
+------------+-----------+
| Ddate | ROOMNO |
+------------+-----------+
| 2018-22-11 | 101 |
| 2018-22-11 | 102 |
| 2018-22-11 | 103 |
| 2018-22-11 | 104 |
+------------+-----------+
TABLE2 (Multiple rows per Room No)
+------------+-----------+------------------+
| Ddate | ROOMNO | MaxVoltage |
+------------+-----------+------------------+
| 2018-22-11 | 101 | 230 |
| 2018-22-11 | 101 | 240 |
| 2018-22-11 | 101 | 250 -----MAX |
| 2018-22-11 | 102 | 230 |
| 2018-22-11 | 102 | 255 -----MAX |
+------------+-----------+------------------+
DESIRED RESULT (I want the Max Voltage for the Room on the Ddate)
+------------+-----------+------------+
| Ddate | ROOMNO | MaxVoltage |
+------------+-----------+------------+
| 2018-22-11 | 101 | 250 |
| 2018-22-11 | 102 | 255 |
| 2018-22-11 | 103 | 235 |
| 2018-22-11 | 104 | 238 |
| 2018-22-11 | 105 | 255 |
+------------+-----------+------------+
SELECT t2.d, t2.roomno, max(t2.maxvolt)
FROM table1 AS t1 JOIN table2 AS t2 ON t1.ddate = t2.ddate
AND t1.roomno = t2.roomno
GROUP BY t2.d, t2.roomno;
use subquery
select t1.dDate,t1.roomno,mvoltage from table1 t1 join
(select Ddate ,roomno,max(MaxVoltage ) as mvoltage from table2
group by Ddate,roomno
) t2 on t1.Ddate=t2.Ddate
Use apply:
select t1.*, t2.maxvoltage
from table1 t1 outer apply
(select top (1) t2.*
from table2 t2
where t2.roomno = t1.roomno and t2.ddate = t1.ddate
order by maxvoltage desc
) t2;
select t1.dDate,t1.roomno, mvoltage from table1 t1 join
(select Ddate ,roomno,max(MaxVoltage ) as mvoltage from table2
group by Ddate,roomno
) t2 on t1.Ddate=t2.Ddate and t1.Roomno = t2.RoomNo
First you will join between two tables in a regular way, using the date and room no.
then use the aggregate function max for the voltage field with over clause , then group by Room No and Date like following
select distinct t1.Ddate, t1.RoomNo, MAX(t2.MaxVoltage) over(partition by t1.RoomNo order by t1.Ddate) MaxVoltage
from Table1 t1
join Table2 t2 on t2.Ddate = t1.Ddate and t2.RoomNo = t1.RoomNo

Join multiple tables using SQL & T-SQL

Unfortunately, I cannot be sure that the name of my question is correct here.
Example of initial data:
Table 1 Table 2 Table 3
| ID | Name | | ID | Info1 | | ID | Info2 |
|----|-------| |----|-------| |----|-------|
| 1 | Name1 | | 1 | text1 | | 1 | text1 |
| 2 | Name2 | | 1 | text1 | | 1 | text1 |
| 3 | Name3 | | 2 | text2 | | 1 | text1 |
| 2 | text2 | | 2 | text2 |
| 3 | text3 |
In my initial data I have relationship between 3 tables by field ID.
I need to join table2 and table3 to the first table, but if I do sequential join, like left join table2 and left join table3 by ID I will get additional records on second join, because there will be several records with one ID after first join.
I need to get records of table2 and table3 like a list in each column for ID of first table.
Here an example of expected result:
Table 3
| ID | Name |Info1(Table2)|Info2(Table3)|
|-------|-----------|-------------|-------------|
| 1 | Name1 | text1 | text1 |
| 1 | Name1 | text1 | text1 |
| 1 | Name1 | null | text1 |
| 2 | Name2 | text2 | text2 |
| 2 | Name2 | text2 | null |
| 3 | Name3 | null | text3 |
This is the method I would use, however, the table design you have could probably be improved on; why are Table2 and Table3 separate in the first place?
USE Sandbox;
GO
CREATE TABLE dbo.Table1 (ID int, [Name] varchar(5))
INSERT INTO dbo.Table1 (ID,
[Name])
VALUES(1,'Name1'),
(2,'Name1'),
(3,'Name3');
CREATE TABLE dbo.Table2 (Id int,Info1 varchar(5));
CREATE TABLE dbo.Table3 (Id int,Info2 varchar(5));
INSERT INTO dbo.Table2 (Id,
Info1)
VALUES(1,'text1'),
(1,'text1'),
(2,'text2'),
(2,'text2');
INSERT INTO dbo.Table3 (Id,
Info2)
VALUES(1,'text1'),
(1,'text1'),
(1,'text1'),
(2,'text2'),
(3,'text3');
WITH T2 AS(
SELECT ID,
Info1,
ROW_NUMBER() OVER (PARTITION BY ID ORDER BY (SELECT NULL)) AS RN --SELECT NULL as you have no other columns to actually create an order
FROM Table2),
T3 AS(
SELECT ID,
Info2,
ROW_NUMBER() OVER (PARTITION BY ID ORDER BY (SELECT NULL)) AS RN
FROM Table3),
Tally AS(
SELECT ROW_NUMBER() OVER (ORDER BY (SELECT NULL)) AS I --Assuming you have 10 or less matching items
FROM (VALUES(NULL),(NULL),(NULL),(NULL),(NULL),(NULL),(NULL),(NULL),(NULL),(NULL)) N(N))
SELECT T1.ID,
T1.[Name],
T2.info1,
T3.info2
FROM Table1 T1
CROSS JOIN Tally T
LEFT JOIN T2 ON T1.ID = T2.ID AND T.I = T2.RN
LEFT JOIN T3 ON T1.ID = T3.ID AND T.I = T3.RN
WHERE T2.ID IS NOT NULL OR T3.ID IS NOT NULL
ORDER BY T1.ID, T.I;
GO
DROP TABLE dbo.Table1;
DROP TABLE dbo.Table2;
DROP TABLE dbo.Table3;
If you have more than 10 rows, then you could build a "proper" tally table on the fly, or create a physical one. One on the fly is probably going to be a better idea though, as I doubt you're going to have 100's of matching rows.

Access SQL: Select from table if ID is in another

Say I have the table T1:
ID | PNo | MM | CP | Flag | Name |
---|-----|------|----|------|------|
1 | 13 | True | 4 | A | X |
1 | 92 | True | 3 | A | X |
2 | 1 | True | 3 | B | Y |
2 | 13 | False| 2 | A | Y |
3 | 13 | True | 3 | B | W |
4 | 1 | True | 3 | B | Z |
And T2:
ID | PNo | MM | CP |
---|-----|------|----|
1 | 13 | True | 4 |
2 | 92 | True | 3 |
3 | 1 | True | 3 |
4 | 13 | False| 2 |
5 | 13 | True | 3 |
1 | 1 | False| 3 |
What I want to do is to do a INSERT INTO where I take values of T1 and T2 but only if the ID of T2 is in T1 and if T1 has the flag with the value A.
I have tried two things:
1) INNER JOIN: Something like
INSERT INTO T3 (ID, PNo, MM, CP, Flag, Name)
SELECT T1.ID, T2.PNo, T2.MM, T2.CP, 'A', T1.Name
FROM T2 INNER JOIN T1 ON T1.ID = T2.ID
WHERE (T1.FLAG = 'A')
The problem here is that it literally takes every combination of all relevant rows from T1 and T2. What I want is, I only want to take those rows of T2 whose IDs are also in T1.
2) IN?
INSERT INTO T3 (ID, PNo, MM, CP, Flag, Name)
SELECT T1.ID, T2.PNo, T2.MM, T2.CP, 'A', T1.Name
FROM T2, T1
WHERE T2.ID IN
(SELECT ID FROM T1 WHERE Flag = 'X')
Problem here is, this takes foreeeeeeeeeeever!
Is there not a more sophisticated method for this?
edit:// Changed a value in T2 so that the example is more meaningful.
So what I want in the new table T3 is:
ID | PNo | MM | CP | Flag | Name |
---|-----|------|----|------|------|
1 | 13 | True | 4 | A | X |
1 | 1 | False| 3 | A | X |
2 | 1 | True | 3 | A | Y |
What I get instead is:
ID | PNo | MM | CP | Flag | Name |
---|-----|------|----|------|------|
1 | 13 | True | 4 | A | X |
1 | 1 | False| 3 | A | X |
1 | 13 | True | 4 | A | X |
1 | 1 | False| 3 | A | X |
2 | 1 | True | 3 | A | Y |
So basically for all T1 values that I select (ID, Name) and for all corresponding rows that I can match by ID in T2, I get every combination.
Your second solution is almost right, however, you perform cartesian product unnecessarily (I'm not surprised that it takes forever). Try this one and if it is slow then create an index on Flag.
INSERT INTO T3 (ID, PNo, MM, CP, Flag, Name)
SELECT *
FROM T2
WHERE T2.ID IN
(SELECT ID FROM T1 WHERE Flag = 'X')
This should do what you want:
INSERT INTO T3 (ID, PNo, MM, CP, Flag, Name)
SELECT T2.ID, T2.PNo, T2.MM, T2.CP, ?, "A"
FROM T2
WHERE T2.ID IN (SELECT ID FROM T1 WHERE Flag = "A");
You are selecting T1.ID in your first query. You can take either T1.ID or T2.ID, because you are requiring that they be equal.
You can do this with a JOIN:
INSERT INTO T3 (ID, PNo, MM, CP, Flag, Name)
SELECT T2.ID, T2.PNo, T2.MM, T2.CP, T1.Name, "A"
FROM T2 INNER JOIN
T1
ON T2.ID = T1.ID;
If this is generating duplicates, then you have multiple rows in T1 for a given name.