Query for earliest datetime and corresponding number field - sql

I'm attempting to update a table with a dollar amount based on the earliest datetime field from another table. For example:
Table 1
ID|INITIAL_ANNUAL_RATE_AMT|
1 | NULL (I want to update this to 25.02)
Table 2
ID|ANNUAL_RATE_AMT|STARTING_DATE|
1 |25.01 |1/1/2014
1 |25.02 |1/1/2013
I've got a query like this that retreives the earliest date from table 2 and the corresponding objects ID:
select ID,
MIN(t2.STARTING_DATE) as EARLIEST_START_DATE
from t2
group by t2.ID
But how can I leverage this into an update statement that sets the INITIAL_ANNUAL_RATE_AMT in table 1 to the earliest corresponding value in table 2?
Something like this (which currently fails):
update t1
set t1.Initial_Annual_Rate__c = t3.ANNUAL_RATE_AMT
from t1, t2
left join
(select t2.ID
MIN(t2.STARTING_DATE) as EARLIEST_START_DATE
from t2
group by t2.DEAL_ID)
as t3 ON (t3.DEAL_ID = t1.DEAL_ID)

One way is to use a CTE
;WITH C AS(
SELECT t.ID, EARLIEST_START_DATE, ANNUAL_RATE_AMT FROM(
select ID,
MIN(t2.STARTING_DATE) as EARLIEST_START_DATE
from #Table2 AS t2
group by t2.ID) t
INNER JOIN #Table2 AS t2 ON t2.ID = t.ID AND t.EARLIEST_START_DATE = t2.STARTING_DATE
)
UPDATE t1
SET INITIAL_ANNUAL_RATE_AMT = C.ANNUAL_RATE_AMT
FROM #Table1 AS t1
INNER JOIN C ON C.ID = t1.ID
SQLFIDDLE

Another method, using a window function to get the first row in each ID partitioned set:
-- Setup test data
declare #table1 table (ID int, INITIAL_ANNUAL_RATE_AMT decimal(9,2))
declare #table2 table (ID int, ANNUAL_RATE_AMT decimal(9,2), STARTING_DATE date)
INSERT INTO #table1 (ID, INITIAL_ANNUAL_RATE_AMT)
SELECT 1, NULL
INSERT INTO #table2 (ID, ANNUAL_RATE_AMT, STARTING_DATE)
SELECT 1,25.01,'1/1/2014'
UNION SELECT 1,25.02,'1/1/2013'
-- Do the update
;with table2WithIDRowNumbers as (
select ID, ANNUAL_RATE_AMT, STARTING_DATE, ROW_NUMBER() OVER (PARTITION BY ID ORDER BY STARTING_DATE) as rowNumber
FROM #table2
)
UPDATE t1
SET INITIAL_ANNUAL_RATE_AMT=t2.ANNUAL_RATE_AMT
FROM table2WithIDRowNumbers t2
INNER JOIN #table1 t1 ON t1.ID=t2.ID
where t2.rowNumber=1
-- Show the result
SELECT * from #table1

Related

How to 1st table distinct and sum and join 2nd table, which if not match between T1 and T2 then add new row

How to join 1st table distinct and sum and join 2nd table, which if not match between T1 and T2 then add new row
Result i need
Period in result table is Getdate()
Select distinct(y.Emp_ID),x.MealAllowance_OT from [dbo].[SPCM_TX_MonthlyAllowance] y left join
(Select x.Emp_Id,x.MealAllowance_OT from [dbo].[SPCM_Cal_OTLog] x
group by x.Emp_Id,x.MealAllowance_OT) x
on x.Emp_Id = y.Emp_ID
order by x.MealAllowance_OT desc
You have to use a subquery on T2 to get sums by group. Then you outer join with T1 to include non-matching rows.
I guessed at the math and the defaults. Like why is Pay/MealAllowance presumed to be 400 when there isn't an entry in T1.
declare #T1 table (Emp_Id int, TRA varchar(3), Pay int)
declare #T2 table (Emp_Id int, DayTy varchar(3), MealAllowance int)
insert into #T1
values (1,'DL',400),(2,'IDL',400),(5,'IDL',400)
insert into #T2
values (1,'DL',18),(2,'IDL',136),(2,'IDL',136),(4,'IDL',136)
select isnull(T1.Emp_Id,T2.Emp_Id) Emp_Id,isnull(T1.TRA,T2.DayTy) DayTY
,isnull(Pay,400) MealAllowance_Mont, isnull(MealAllowance,0) MealAllowance
,isnull(Pay,400) + isnull(MealAllowance,0) Tot, getdate() period
from #T1 T1
full outer join
(
select Emp_Id, DayTy,sum(MealAllowance) MealAllowance
from #T2
group by Emp_Id, DayTy
) T2
on T1.Emp_Id = T2.Emp_Id
and T1.TRA = T2.DayTy

Get the list of name column values which are not common in both the tables?

recently i gave an interview where the question was
suppose there are two tables in database.
Table T1 has a column named "name" in it and few other columns
Table T2 also has a column name "name" and few other columns
suppose table T1 has values in name column as
[n1,n2,n3,n4,n5]
and values in the "name" column of table T2 are
[n2,n4]
then output should be
[n1,n3,n5] as n2 and n4 are common in both tables
we needs to find the list of names which are not common in both the tables.
The solution that i provided him was using join in the below form
select name from table1 where name not in (select t1.name from table1 t1 join table2 t2 on t1.name=t2.name)
UNION
select name from table2 where name not in (select t1.name from table1 t1 join table2 t2 on t1.name=t2.name)
But he said there is still a better solution. I was not able to come up with any different and more efficient solution. What is the other efficient way to get the list of names if there is any?
If the NAME column does not have NULL values, there is also
select distinct(coalesce(a.name, b.name)) name
from table1 a
full join table1 b on a.name = b.name
where a.name is null or b.name is null
(Corrected WHERE condition, sorry...)
Use FULL OUTER JOIN:
SELECT DISTINCT(COALESCE(t1.NAME, t2.NAME)) AS NAME
FROM TABLE1 t1
FULL OUTER JOIN TABLE2 t2
ON t2.NAME = t1.NAME
WHERE t1.NAME IS NULL OR
t2.NAME IS NULL
A FULL OUTER JOIN is similar to a LEFT OUTER JOIN unioned with a RIGHT OUTER JOIN - it returns rows where data exists in the first table but not the second, or where it data exists in the second table but not the first. You could get the same effect by using
SELECT t1.NAME
FROM TABLE1 t1
LEFT OUTER JOIN TABLE2 t2
ON t2.NAME = t1.NAME
WHERE t2.NAME IS NULL
UNION
SELECT t2.NAME
FROM TABLE1 t1
RIGHT OUTER JOIN TABLE2 t2
ON t2.NAME = t1.NAME
WHERE t1.NAME IS NULL
and in fact the above is what you'd need to do if you were using a database which doesn't support the FULL OUTER JOIN syntax (e.g. MySQL, the last time I looked).
See this dbfiddle
Union the tables and return the values that don't have a count of 2:
create table t1 (
c1 int
);
create table t2 (
c1 int
);
insert into t1 values ( 1 );
insert into t1 values ( 3 );
insert into t2 values ( 2 );
insert into t2 values ( 3 );
commit;
select c1 only_in_one_table
from (
select 'T1' t, c1 from t1
union
select 'T2' t, c1 from t2
)
group by c1
having count(*) <> 2;
ONLY_IN_ONE_TABLE
1
2
I'm not a fan of not in with subqueries, because it behaves unexpectedly with null values. And the person asking would have to explain what "better" means. Your version is actually reasonable.
I might be inclined to approach this using aggregation:
select name
from ((select distinct name, 1 as in_table1, 0 as in_table2
from table1
) union all
(select distinct name, 0 as in_table1, 0\1 as in_table2
from table2
)
) t
group by name
having max(in_table1) <> max(in_table2);
In a real world case, you would probably have a separate table with all names. If so:
select n.*
from names n
where (not exists (select 1 from table1 t1 where t1.name = n.name) and
exists (select 1 from table2 t2 where t2.name = n.name
) or
(exists (select 1 from table1 t1 where t1.name = n.name) and
not exists (select 1 from table2 t2 where t2.name = n.name
);
This is usually the fastest approach because it does not involve any aggregation or duplicate removal.
If you want to use SET operator then find the solution as below:
CREATE TABLE TABLE1(NAME VARCHAR2(100));
CREATE TABLE TABLE2(NAME VARCHAR2(100));
INSERT INTO TABLE1 VALUES('A');
INSERT INTO TABLE1 VALUES('B');
INSERT INTO TABLE1 VALUES('C');
INSERT INTO TABLE2 VALUES('A');
INSERT INTO TABLE2 VALUES('B');
INSERT INTO TABLE2 VALUES('D');
SELECT
NAME
FROM
(
SELECT
NAME
FROM
TABLE1
UNION
SELECT
NAME
FROM
TABLE2
)
WHERE
NAME NOT IN (
SELECT
NAME
FROM
TABLE1
JOIN TABLE2 USING ( NAME )
);
Cheers!!
Yet another possible solution:
Find those that are in the first table but not in the second table using the MINUS operator (which is Oracle's implementation of the standard EXCEPT). Then UNION that with those that are in the second but not in the first.
(
select name
from t1
minus
select name
from t2
)
union all
(
select name
from t2
minus
select name
from t1
);
Given this setup:
create table t1
(
name varchar(10)
);
insert into t1 values ('Arthur');
insert into t1 values ('Zaphod');
create table t2
(
name varchar(10)
);
insert into t2 values ('Tricia');
insert into t2 values ('Zaphod');
This returns:
NAME
------
Arthur
Tricia
select id from((select id from table1)
union all
(select id from table2)) as t1
group by id having count(id)=1
Using the basic set operations the following query should work.
(
select name from table1
union all
select name from table2
)
minus
(
select name from table1
intersect
select name from table2
)
;
Regards
Akash

SQL. Joining records from 3 or more tables based on dates

My tables look like this(just an example):
table1:
TIME |data1
1.01.2018|aaa
2.01.2018|bbb
table2:
TIME |data2
1.01.2018|abcd
2.01.2018|cd
table3:
TIME |data3
1.01.2018|
2.01.2018|d
Now what i would like to do is take the data from table2 and put it in the table1 where there are missing records. If there is no record in all tables for that date, then the data in data column is NULL. The data copied to table1 needs to be the same date as in the table2. and if there is no same date in table1 as it is in table2, that date is created.
So far i have tried with this code, but the data copied is not valid. If there is a missing day in some of the tables, that missing day is created in time column, but the data in data column is incorect.
SELECT DISTINCT table1.time,table1.data,table2.time,table2.data,table3.time,table3.data
FROM table1
LEFT JOIN table2 ON table1.time=table2.time
LEFT JOIN table3 ON table1.time=table3.time
EDIT
This is how the output should look like:
For example, there was no data in table3 for data3 for that specific date, so the cell is empty.
TIME |data1|data2|data3|
1.01.2018|aaa |abcd | |
2.01.2018|bbb | cd | d |
You need a full join instead of a left join. (you didn't specify the backend. Here the sample is with MS SQL) ie:
SELECT
coalesce(table1."time",table2."time", table3."time") as "time",
table1.data1,table2.data2,table3.data3
FROM table1
full JOIN table2 ON table1."time"=table2."time"
full JOIN table3 ON table1."time"=table3."time" ;
Output:
Time data1 data2 data3
1.01.2018 aaa abcd (null)
2.01.2018 bbb cd d
EDIT: The distinct in your code suggests you want only one row per day. Then it is not clear which column value should be used. Assuming any would do, you can do aggregation like this:
SELECT
coalesce(table1."time",table2."time", table3."time") as "time",
max(table1.data1) as data1,
max(table2.data2) as data2,
max(table3.data3) as data3
FROM table1
full JOIN table2 ON table1."time"=table2."time"
full JOIN table3 ON table1."time"=table3."time"
group by coalesce(table1."time",table2."time", table3."time");
Start the process with
SELECT [DISTINCT] TIME FROM TABLE1
UNION [DISTINCT]
SELECT [DISTINCT] TIME FROM TABLE2
UNION [DISTINCT]
SELECT [DISTINCT] TIME FROM TABLE3
And then LEFT JOIN the three tables to that.
Try this:
with cte as (Select time from table1 union Select time from table2 union Select time from table3)
SELECT cte.time,table1.data as data1,table2.data as data2,table3.data as data3
FROM cte
LEFT JOIN table1 ON table1.time=cte.time
LEFT JOIN table2 ON cte.time=table2.time
LEFT JOIN table3 ON cte.time=table3.time
Apply simple left join after union on.
DECLARE #t1 table
(
TIMEDate1 DATE
,Data1 VARCHAR(50)
)
DECLARE #t2 table
(
TIMEDate2 DATE
,Data2 VARCHAR(50)
)
DECLARE #t3 table
(
TIMEDate3 DATE
,Data3 VARCHAR(50)
)
insert #t1 (TIMEDate1,Data1 ) select getdate(),'aaa'
insert #t1 (TIMEDate1,Data1 ) select getdate()+1,'bbb'
insert #t2 (TIMEDate2,Data2 ) select getdate(),'abcd'
insert #t2 (TIMEDate2,Data2 ) select getdate()+1,'cd'
insert #t3 (TIMEDate3,Data3 ) select getdate(),''
insert #t3 (TIMEDate3,Data3 ) select getdate()+1,'d'
SELECT t.TIMEDate,t1.Data1,t2.Data2,t3.Data3
FROM (
SELECT distinct TIMEDate1 as TIMEDate FROM #t1
UNION
SELECT distinct TIMEDate2 as TIMEDate FROM #t2
UNION
SELECT distinct TIMEDate3 as TIMEDate FROM #t3
) t
left JOIN #t1 t1 ON t1.TIMEDate1 = t.TIMEDate
left JOIN #t2 t2 ON t2.TIMEDate2 = t.TIMEDate
left JOIN #t3 t3 ON t3.TIMEDate3 = t.TIMEDate
--order by t1.TIMEDate1 asc
The SQL below is just an alternative for inserting the missing dates into Table1.
--
-- insert those into Table1 that are in Table2 but not in Table1
--
insert into Table1 ("time", data1)
select "time", data2
from Table2 as t2
where not exists
(
select 1
from Table1 as t1
where t1."time" = t2."time"
);
--
-- insert those into Table1 that are in Table3 but not in Table1
--
insert into Table1 ("time", data1)
select "time", data3
from Table3 as t3
where not exists
(
select 1
from Table1 as t1
where t1."time" = t3."time"
);
Then add the dates that are still missing in Table1 after all that.
Below is an example for MS SQL Server :
;with RCTE_DATERANGE as
(
select min([time]) as dt, max([time]) as max_dt
from Table1
union all
select dateadd(day, 1, dt), max_dt
from RCTE_DATERANGE where dt < max_dt
)
insert into Table1 ([time])
select dt
from RCTE_DATERANGE AS rcte
where not exists
(
select 1
from Table1 as t1
where t1.[time] = rcte.dt
);
-- Just checking
;with RCTE_DATERANGE as
(
select min([time]) as dt, max([time]) as max_dt
from Table1
union all
select dateadd(day, 1, dt), max_dt
from RCTE_DATERANGE where dt < max_dt
)
select rcte.dt, t1.[time] as time1, t2.[time] as time2, t3.[time] as [time3], t1.data1, t2.data2, t3.data3
from RCTE_DATERANGE AS rcte
full join Table1 t1 on t1.[time] = rcte.dt
full join Table2 t2 on t2.[time] = rcte.dt
full join Table3 t3 on t3.[time] = rcte.dt;

Use multiple WITH tablename AS (...) statements SQL Server

I'm trying to create two temporary tables and join them with a permanent table. For example:
WITH temp1 AS (COUNT(*) AS count_sales, ID FROM table1 GROUP BY ID)
WITH temp2 AS (COUNT(*) AS count_somethingelse, ID FROM table2 GROUP BY ID)
SELECT *
FROM table3 JOIN table2 JOIN table1
ON table1.ID = table2.ID = table3.ID
but there seems to be an issue having multiple WITH tablename AS (...) statments. I tried a semicolon.
Your query should look more like this:
WITH temp1 AS (
SELECT COUNT(*) AS count_sales, ID
FROM table1
GROUP BY ID
),
temp2 AS (
SELECT COUNT(*) AS count_somethingelse, ID
FROM table2
GROUP BY ID
)
SELECT *
FROM temp2 JOIN
temp1
ON temp1.ID = temp2.ID;
Your query has multiple errors. I would suggest you start by understanding why this version works -- or at least does something other than report on syntax errors. Then, go back and study SQL some more.
I'm trying to create two temporary tables
Just for clarity... you used a CTE which is not quite the same thing as a temporary table. You've also tagged 'temp tables', so you want a temp table? You can store query results in a declared table variable or an actual temp table.
An example of declared table variables:
DECLARE #table1 TABLE(id int, count_sales int)
INSERT INTO #table1 (id, count_sales)
SELECT ID, COUNT(*)
FROM table1
GROUP BY ID
--or however you populate temp table1
DECLARE #table2 TABLE(id int, count_somethingelse int)
INSERT INTO #table2 (id, count_somethingelse)
SELECT ID, COUNT(*)
FROM table2
GROUP BY ID
--or however you populate temp table2
SELECT T3.id
--,T2.(some column)
--,T1.(some column)
--,etc...
FROM table3 T3 INNER JOIN #table2 T2 ON T3.id = T2.id
INNER JOIN #table1 T1 ON T3.id = T1.id

Update values from multiple selected rows

I have 2 tables. Both have the same amount of rows.
Sample data:
Table1{
Id, IdTable2Row
}
Table2 {
Id, RedChicken -- LOL
}
Each row of Table1 (column IdTable2Row) should get the id of 1 row of table 2 (irrelevant which one).
Table1.IdTable2Row is null in every row before the operation.
How can I do something like this?
UPDATE Table1 SET IdTable2Row = (SELECT Id FROM [Table2])
update T1
set idtablerow = t2.id
from Table1 T1
inner join Table2 T2
on T1.something = T2.something
In your case, with no common column:
with T1 as
(
select Table1.*, row_number() over (order by anycolumn) rn
from Table1
)
update T1
set idtablerow = t2.id
from T1
inner join
(
select x1.*, row_number() over (order by id) rn
from Table2 x1
) T2
on t1.rn = t2.rn
Should work
If I understand correctly you need to match rows 1 to 1. Here is a solution I come up with:
declare #Table1 table(
Id INT,
IdTable2Row INT NULL
)
declare #Table2 table(
Id INT, RedChicken varchar(10) -- LOL
)
INSERT INTO #Table1
VALUES
(
1,NULL
),(
2,NULL
),(
3,NULL
);
INSERT INTO #Table2
VALUES
(
10,'s'
),(
20,'a'
),(
30,'b'
);
SELECT *
FROM #Table1 ;
SELECT *
FROM #Table2;
WITH cte1 AS
(
SELECT
ROW_NUMBER() over(ORDER by Id) AS rown
,Id
,IdTable2Row
FROM #Table1
),
cte2 AS
(
SELECT ROW_NUMBER() over(ORDER by Id) AS rown
,Id
,RedChicken
FROM #Table2
)
UPDATE t
SET IdTable2Row = c2.Id
FROM #Table1 t
JOIN cte1 c1 ON c1.Id = t.Id
JOIN cte2 c2 ON c1.rown = c2.rown
SELECT *
FROM #Table1
IN SQL SERVER if there is common column then you can join the table and update
update a set a.IdTable2Row=b.Id from
Table1 a ,Table2 b
where a.column=b.column