Join two columns and pick just one from the child results - sql-server

I want to perform a JOIN on two tables to get the LEADTIME per ITEMNUM.
Both tables have common value ITEMNUM that I use for the JOIN operation.
The problem is that in the second table the ITEMNUM is not unique and can contain multiple. different LEADTIME values.
For example see ITEMNUM 2 in Table 2.
In case there are multiple LEADTIME values, I just want to get one of the LEADTIME values.
I don't care which one.
This is what I have so far, but it keeps returning multiple lines for ITEMNUM 2
SELECT ITEMNUM, LEADTIME
FROM TABLE1
LEFT JOIN TABLE2 on TABLE2.ITEMNUM = TABLE1.ITEMNUM
So what can I do to get just one LEADTIME for ITEMNUM 2? ( as mentioned, I don't care which value )

This approach assigns a row number to each row in #table2 resetting it for each ItemNum value. You need to have an order by clause (if you don't SQL Server raises an error) so I am ordering by NEWID() which should result in a randomized order. You will likely want to tweak what columns you are returning. Here is the dbfiddle to see it in action.
IF OBJECT_ID('tempdb.dbo.#table1', 'U') IS NOT NULL DROP TABLE #table1;
IF OBJECT_ID('tempdb.dbo.#table2', 'U') IS NOT NULL DROP TABLE #table2;
CREATE TABLE #table1
(
ID INT
, ItemNum INT
);
CREATE TABLE #table2
(
ID INT
, ItemNum INT
, LeadTime INT
);
INSERT INTO #table1 VALUES (1, 1)
INSERT INTO #table1 VALUES (2, 2)
INSERT INTO #table1 VALUES (3, 3)
INSERT INTO #table1 VALUES (4, 4)
INSERT INTO #table1 VALUES (5, 5)
INSERT INTO #table2 VALUES (1, 1, 6)
INSERT INTO #table2 VALUES (2, 2, 7)
INSERT INTO #table2 VALUES (3, 2, 2)
INSERT INTO #table2 VALUES (4, 3, 6)
INSERT INTO #table2 VALUES (5, 4, 3)
SELECT *
FROM #table1 AS t1
LEFT JOIN (
SELECT *
, ROW_NUMBER() OVER (PARTITION BY ItemNum ORDER BY NEWID()) AS rn
FROM #table2
) AS t2 ON t1.ItemNum = t2.ItemNum
AND t2.rn = 1;

There are several ways to get this done.I would use OUTER APPLY with TOP.
DROP TABLE IF EXISTS #Table1
CREATE TABLE #Table1
(
Id INT
, ItemNum INT
)
DROP TABLE IF EXISTS #Table2
CREATE TABLE #Table2
(
Id INT
, ItemNum INT
, LeadTime INT
)
INSERT INTO #Table1 VALUES
(1, 1)
, (2, 2)
, (3, 3)
, (4, 4)
, (5, 5)
INSERT INTO #Table2 VALUES
(1, 1, 6)
, (2, 2, 7)
, (3, 2, 2)
, (4, 3, 6)
, (5, 4, 3)
SELECT
*
FROM
#Table1 AS T1
OUTER APPLY
(
SELECT TOP 1 T2.LeadTime FROM #Table2 AS T2 WHERE T2.ItemNum = T1.ItemNum
) AS LT

Related

Recursive CTE with hierarchy data

Sample data:
declare #docs table (docid int, name varchar(10), isfolder int)
declare #hierarchy table (childid int, parent varchar(10))
insert #docs values (1, 'Doc1', 0)
insert #docs values (2, 'Doc2', 0 )
insert #docs values (3, 'Folder1', 1 )
insert #docs values (4, 'Folder2', 1 )
insert #docs values (5, 'SubFolderA', 1 )
insert #docs values (6, 'SubFolderB', 1 )
insert #hierarchy values (1, 5)
insert #hierarchy values (1, 6)
insert #hierarchy values (2, 6)
insert #hierarchy values (5, 3)
insert #hierarchy values (6, 4)
I want to list the data above so I end up with the documents (isfolder = 0) and the folder paths that they are in.
Expected output:
DocId Name Path
--------------------------------------
1 Doc1 Folder1\SubFolderA
1 Doc1 Folder2\SubFolderB
2 Doc2 Folder2\SubFolderB
CTE
I started to write this next CTE as it said this can be used to create my output but I am wrong somewhere
;WITH folderCTE (docid, name) AS
(
-- Anchor member
SELECT docid, CAST(name AS varchar(max)) AS name
FROM #docs
WHERE isfolder = 0
UNION ALL
-- Recursive member that references expression_name.
SELECT d.docid, CAST(folderCTE.Name + '\' + d.name AS varchar(max)) AS name
FROM folderCTE
INNER JOIN #docs d ON d.docid = folderCTE.docid
)
-- references expression name
SELECT *
FROM folderCTE
Can anyone show me how I should be doing a CTE for this?
If you use a CTE which just expands the hierarchy for folders, and a second one to build the hierarchy, you can simply join to this to get the folder for your docs:
declare #docs table (docid int, name varchar(10), isfolder int)
declare #hierarchy table (childid int, parentid varchar(10))
insert #docs values (1, 'Doc1', 0)
insert #docs values (2, 'Doc2', 0 )
insert #docs values (3, 'Folder1', 1 )
insert #docs values (4, 'Folder2', 1 )
insert #docs values (5, 'SubFolderA', 1 )
insert #docs values (6, 'SubFolderB', 1 )
insert #docs values (7, 'SuperFold1', 1 )
insert #hierarchy values (1, 5)
insert #hierarchy values (1, 6)
insert #hierarchy values (2, 6)
insert #hierarchy values (5, 3)
insert #hierarchy values (6, 4)
insert #hierarchy values (3, 7)
;WITH folderCTE
AS
(
select docid, cast(name as nvarchar(max)) as name, parentid
from #docs d
left join #hierarchy h on d.docid=h.childid
where d.isfolder = 1
),
folderHierarchyCTE
as
(
select docid, cast(name as nvarchar(max)) as name
from folderCTE where parentid is null
union all
select d.docid, cast(p.name + '/' + d.name as nvarchar(max))
from folderCTE d
inner join folderHierarchyCTE p on d.parentid = p.docid
)
SELECT d.docid, d.name, f.name
FROM #docs d
inner join #hierarchy h on h.childid=d.docid
inner join folderHierarchyCTE f on h.parentid=f.docid
where d.isfolder = 0

Repeat the column value until value change in same column value

My table structure:
declare #TestTable as table
(
id int,
somedate date,
somevalue int
)
insert into #TestTable values
(1, '2019-01-01', 1000),
(2, '2019-01-02', null ),
(3, '2019-01-03', null),
(4, '2019-01-04', null ),
(5, '2019-01-05', 800),
(6, '2019-01-06', null),
(7, '2019-01-07', null),
(8, '2019-01-08', null),
(9, '2019-01-09', null),
(10, '2019-01-10', 700)
Repeat the column value until any change in value of column (somevalue).. Is it possible with window functions?
Required output:
You can achieve that by using window function as well.
try the following:
select id, somevalue, newvalue=max(somevalue) over (partition by c)
from
(
select id, somevalue
,c=count(somevalue) over (order by id)
from #testtable
) t
order by id;
Please find the demo here.
Try this below logic-
DEMO HERE
SELECT A.*,
(
SELECT somevalue
FROM #TestTable
WHERE id = (
SELECT MAX(id)
FROM #TestTable
WHERE id <= A.id
AND somevalue IS NOT NULL
)
) new_column
FROM #TestTable A
ORDER BY A.id
You can achieve this using COALESCE (Transact-SQL)
.
SELECT
[id]
, somedate
, COALESCE(somevalue,
(SELECT TOP (1) somevalue
FROM #TestTable AS p2
WHERE
p2.somevalue IS NOT NULL
AND p2.[id] <= p.[id] ORDER BY p2.[id] DESC))
FROM #TestTable AS p;
Here is the live db<>fiddle demo.

Converting Temporary table code to CTE to improve performance

I have the following code in my Stored procedure:
CREATE MyProc
AS
CREATE TABLE #Table1
(
Field1 INT NOT NULL,
Field2 VARCHAR NULL,
Field3 VARCHAR NULL,
Field4 VARCHAR NULL
);
-----Populate Temporary Table-----
INSERT INTO #Table1 (Field1)
SELECT val1 FROM Tab1
INSERT INTO #Table1 (Field1,Field2)
SELECT val1,"val2" FROM Tab2
INSERT INTO #Table1 (Field1,Field2,Field3)
SELECT val1,"val2","val3" FROM Tab3
INSERT INTO #Table1 (Field1,Field2,Field3,Field4)
SELECT val1,"val2","val3","val4" FROM Tab3
SELECT Field1,Field2,Field3,Field4 FROM #Table1
DROP #Table1
There are around 10-15 INSERT statements which are populating the temporary table.I want to tune the stored proc to improve performance using CTE or some other way.How I can rewrite the procedure?
i'm not entirely sure this is going to be better in any way but this is using a cte to get the same output (i used some lazy-ass value arrays to get it to work out of the box, you can ofcourse substitute those with actual tables):
with cte (f1, f2, f3, f4)
as (
select 1, null, null, null
from (
values (1)
) t1(n1)
union all
select 1, 2, null, null
from (
values (1, 2)
) t1(n1, n2)
union all
select 1, 2, 3, null
from (
values (1, 2, 3)
) t1(n1, n2, n3)
union all
select 1, 2, 3, 4
from (
values (1, 2, 3, 4)
) t1(n1, n2, n3, n4)
)
select *
from cte

How to update table when rows of one table are different from the other

I have a table table1 and a temporary table temp2. Temp2 contains updated values which i want to update in table1. So, for any rows that are different i want to update the values from Temp2 to table 1. I tried something like this but its not working.
update Role_Master set Role_Desc=Role_Descc , Role_Version_Number =Role_Version_Number+1,Role_Dept=Role_Deptt,Role_All_Clients=Role_All_Clientss,
Role_Admin=Role_Adminn,Role_Super_Admin=Role_Super_Adminn,Role_Modified_Date = GETDATE(),Role_Modified_By = 'T6086' FROM #TEMP1 where Role_ID in
(SELECT #TEMP1.Role_IDD FROM #TEMP1 LEFT JOIN Role_Master ON (#TEMP1.Role_Descc = Role_Master.Role_Desc and #Temp1.Role_Deptt=Role_Master.Role_Dept)
WHERE Role_Master.Role_Desc is null and Role_Master.Role_Dept IS NULL)
hard to help you without knowing the schema of the two tables ... but it should be possible to join the two tables and decide by a where condition which rows to update ... check out this simple example ... maybe it helps
create table #temp1 (id int, val nvarchar(100))
create table #temp2 (id int, val nvarchar(100))
insert into #temp1 (id, val) values (1, 'eins')
insert into #temp1 (id, val) values (2, 'eins')
insert into #temp1 (id, val) values (3, 'eins')
insert into #temp2 (id, val) values (1, 'zwei')
insert into #temp2 (id, val) values (2, 'eins')
insert into #temp2 (id, val) values (3, 'eins')
update #temp1 set #temp1.val = b.val
from #temp1 a join #temp2 b on a.id = b.id
where a.val <> b.val
select ##rowcount -- returns 1 because 1 row was updated
select * from #temp1

How do I find records out of order - SQL?

Let's say I have a table with an ID Identity column, some data, and a datestamp. Like this:
1 data 5/1/2013 12:30
2 data 5/2/2013 15:32
3 data 5/2/2013 16:45
4 data 5/3/2013 9:32
5 data 5/5/2013 8:21
6 data 5/4/2013 9:36
7 data 5/6/2013 11:42
How do I write a query that will show me the one record that is timestamped 5/4? The table has millions of records. I've done some searching, but I don't know what to call what I'm searching for. :/
declare #t table(id int, bla char(4), timestamp datetime)
insert #t values
(1,'data','5/1/2013 12:30'),
(2,'data','5/2/2013 15:32'),
(3,'data','5/2/2013 16:45'),
(4,'data','5/3/2013 9:32'),
(5,'data','5/5/2013 8:21'),
(6,'data','5/4/2013 9:36'),
(7,'data','5/6/2013 11:42')
select timestamp
from
(
select rn1 = row_number() over (order by id),
rn2 = row_number() over (order by timestamp), timestamp
from #t
) a
where rn1 not in (rn2, rn2-1)
in 2008 r2, this would be a way
DECLARE #Table AS TABLE
(id INT , ladate DATETIME)
INSERT INTO #Table VALUES (1, '2013-05-01')
INSERT INTO #Table VALUES (2, '2013-05-02')
INSERT INTO #Table VALUES (3, '2013-05-03')
INSERT INTO #Table VALUES (4, '2013-05-05')
INSERT INTO #Table VALUES (5, '2013-05-04')
INSERT INTO #Table VALUES (6, '2013-05-06')
INSERT INTO #Table VALUES (7, '2013-05-07')
INSERT INTO #Table VALUES (8, '2013-05-08')
--I added the records in the sort order but if not just make sure you are sorted in the query
SELECT t2.ladate FROM #Table T1
INNER JOIN #Table T2 ON T1.Id = T2.Id + 1
INNER JOIN #Table t3 ON t2.id = t3.id + 1
WHERE t3.ladate < t2.ladate AND t2.ladate > t1.ladate
-- I made the assumption that your Id are all there, 1,2,3,4,5.... none missing... if there are rownumbers missing, you can use row_number()

Resources