How to Select one Value for each row after Joining of 2 Tables - sql

I have 2 tables, the first one has 10 distinct values:
,
each GlobalPnID has many values on the second table, I want to join 2 tables and select one random value of PortionKey of the second table that match the condition and move to the next GlobalPnID
SELECT TOP 10 gpnp.PortionKey, tt.GlobalPnID
from #TempTable tt
LEFT JOIN [dbo].[GlobalPartNumberPortions] gpnp ON gpnp.GlobalPnId = tt.GlobalPnID
-- tt is the first table
-- gpnp is the second

SELECT TT.GlobalPnID,X.PortionKey
FROM #TempTable AS TT
CROSS APPLY
(
SELECT TOP 1 R.PortionKey
FROM [dbo].[GlobalPartNumberPortions] AS R
WHERE R.GlobalPnId=TT.GlobalPnID
ORDER BY R.PortionID
)X

You could use Row_Number with a CTE and set the criteria you want, for example:
DECLARE #TempTable TABLE
(
globalpnid INT
)
DECLARE #GlobalPartNumberPortions TABLE
(
portionid INT,
portionkey NVARCHAR(10),
globalpnid INT
)
INSERT INTO #TempTable
(globalpnid)
VALUES (1),(2),(3),(4)
INSERT INTO #GlobalPartNumberPortions
(portionid,
portionkey,
globalpnid)
VALUES (1,'ABC',1),
(2,'XYZ',1),
(3,'AZZ',2),
(4,'QWE',3),
(5,'TYU',4);
WITH cteportion
AS (SELECT portionkey,
globalpnid,
rn = Row_number()
OVER (
partition BY globalpnid
ORDER BY RAND(CHECKSUM(NEWID()))))
FROM #GlobalPartNumberPortions)
SELECT gpnp.portionkey,
tt.globalpnid
FROM #TempTable tt
LEFT JOIN cteportion gpnp
ON tt.globalpnid = gpnp.globalpnid
AND gpnp.rn = 1
This will partition the second table by the globalpnid ordering on ORDER BY RAND(CHECKSUM(NEWID()))) and you can then use this in the join gpnp.rn = 1. In the example I've included, you'll see that GlobalPnID = 1 will alternate between ABC and XYZ.
Edit: as suggested by #Thorsten Kettner in the comment, you can order by RAND(CHECKSUM(NEWID())))

Related

Count number of cases where visitor rank is higher on one page then on another

I want to count number fullvisitorID where rank in /page_y is higher then rank in page_x. So in this case result would be 1, only 111
fullvisitorID
rank
page
111
1
/page_x
111
2
/page_y
222
1
/page_x
222
2
/page_x
333
2
/page_x
333
1
/page_y
Consider below approach
select count(*) from (
select distinct fullvisitorID
from your_table
qualify max(if(page='/page_y',rank,null)) over win > max(if(page='/page_x',rank,null)) over win
window win as (partition by fullvisitorID)
)
SELECT COUNTIF(page = '/page_y') cnt FROM (
SELECT * FROM sample_table WHERE page IN ('/page_x', '/page_y')
QUALIFY ROW_NUMBER() OVER (PARTITION BY fullvisitorID ORDER BY rank DESC) = 1
);
for count you can use COUNT and GROUP BY
SELECT fullvisitorID, COUNT(fullvisitorID), Page FROM table t1
WHERE rank = (SELECT MAX(t2.rank) FROM table t2 WHERE t2.fullvisitorID = t1.fullvisitorID)
Group By fullvisitorID, Page
You can apply a SELF JOIN between the two tables, by matching on the "fullvisitorID" field, then force
the first table to have "page_y" values
the second table to have "page_x" values
rank of the first table to have higher rank of the second table
SELECT *
FROM tab t1
INNER JOIN tab t2
ON t1.fullvisitorID = t2.fullvisitorID
AND t1.page = '/page_y'
AND t2.page = '/page_x'
AND t1.rank > t2.rank
Table separation approach:
DECLARE #t1 TABLE ( fullvisitorID INT, [rank] INTEGER,[page] VARCHAR (max)) --here where page = x
DECLARE #t2 TABLE ( fullvisitorID INT, [rank] INTEGER,[page] VARCHAR (max)) --here where page = y
INSERT INTO #t1 SELECT * FROM #test t WHERE t.[page] LIKE '/page_x'
INSERT INTO #t2 SELECT * FROM #test t WHERE t.[page] LIKE '/page_y'
SELECT COUNT(*) FROM #t1 INNER JOIN #t2 ON [#t1].fullvisitorID = [#t2].fullvisitorID WHERE [#t1].rank < [#t2].rank

Finding Occurrence of the duplicate values

I have table with 3 columns (id, Name, Occurrence), I want to update the Occurrence column ,based on the id column, attached snap for the reference.
for example if my id column has "606" value 3 times then my occurrent column should have 3 against all the "606" value.
Below is the method which I tried.
I tried to find the duplicate values using group by and Having clause and saved it in a temp table and from there I tried to join the table value from the temp table.
you can use window functions in an updatable CTE for this.
You haven't supplied any actual sample data so this is untested, however the following should work:
with x as (
select Id, Occurence, count(*) over(partition by Id) qty
from Table
)
update x
set Occurence = Qty;
You can go for GROUP BY based approach also.
declare #TABLE TABLE(ID INT, NAME CHAR(3), occurance int null)
insert into #TABLE VALUES
(1,'AAA',NULL),(1,'AAA',NULL),(2,'CCC',NULL),(3,'DDD',NULL), (3,'DDD',NULL),(4,'EEE',NULL),(5,'FFF',NULL);
;WITH CTE_Table as
(
SELECT ID, COUNT(*) AS Occurance
FROM #table
group by id
)
UPDATE t
SET occurance = c.occurance
FROM #table t
INNER JOIN CTE_Table as c
on C.ID = T.ID
SELECT * FROM #TABLE
ID
NAME
occurance
1
AAA
2
1
AAA
2
2
CCC
1
3
DDD
2
3
DDD
2
4
EEE
1
5
FFF
1
You can use a CTE and calculate row number and update your table base on CTE
;WITH q
AS
(
SELECT Id,COUNT(1) 'RowNum'
FROM YourTable
GROUP BY Id
)
UPDATE YourTable
SET Occurrence=q.RowNum
FROM YourTable t
INNER JOIN q
ON t.Id=q.Id

How to get last record from Master-Details tables

I have a table that has 3 columns.
create table myTable
(
ID int Primary key,
Detail_ID int references myTable(ID) null, -- reference to self
Master_Value varchar(50) -- references to master table
)
this table has the follow records:
insert into myTable select 100,null,'aaaa'
insert into myTable select 101,100,'aaaa'
insert into myTable select 102,101,'aaaa'
insert into myTable select 103,102,'aaaa' ---> last record
insert into myTable select 200,null,'bbbb'
insert into myTable select 201,200,'bbbb'
insert into myTable select 202,201,'bbbb' ---> last record
the records is saved In the form of relational with ID and Detail_ID columns.
I need to select the last record each Master_Value column. follow output:
lastRecordID Master_Value Path
202 bbbb 200=>201=>202
103 aaaa 100=>101=>102=>103
tips:
The records are not listed in order in the table.
I can not use the max(ID) keyword. beacuse data is not sorted.(may
be the id column updated manually.)
attempts:
I was able to Prepare follow query and is working well:
with Q as
(
select ID ,Detail_ID, Master_Value , 1 RowOrder, CAST(id as varchar(max)) [Path] from myTable where Detail_ID is null
union all
select R.id,R.Detail_ID , r.Master_Value , (q.RowOrder + 1) RowOrder , (q.[Path]+'=>'+CAST(r.id as varchar(max))) [Path] from myTable R inner join Q ON Q.ID=R.Detail_ID --where r.Dom_ID_RowType=1010
)
select * into #q from Q
select Master_Value, MAX(RowOrder) lastRecord into #temp from #Q group by Master_Value
select
q.ID lastRecordID,
q.Master_Value,
q.[Path]
from #temp t
join #q q on q.RowOrder = t.lastRecord
where
q.Master_Value = t.Master_Value
but I need to simple way (one select) and optimal method.
Can anyone help me?
One method uses a correlated subquery to get the last value (which is how I interpreted your question):
select t.*
from mytable t
where not exists (select 1
from mytable t2
where t2.master_value = t.master_value and
t2.id = t.detail_id
);
This returns rows that are not referred to by another row.
For the path, you need a recursive CTE:
with cte as (
select master_value, id as first_id, id as child_id, convert(varchar(max), id) as path, 1 as lev
from mytable t
where detail_id is null
union all
select cte.master_value, cte.first_id, t.id, concat(path, '->', t.id), lev + 1
from cte join
mytable t
on t.detail_id = cte.child_id and t.master_value = cte.master_value
)
select cte.*
from (select cte.*, max(lev) over (partition by master_value) as max_lev
from cte
) cte
where max_lev = lev
Here is a db<>fiddle.

Check two table's records are in the same order

I have two recordset (temp table data) with some columns. I need to check that both table's records are in the same order.
I am not checking differences between two recordset or common rows. I need to check that they are in the same order.(both tables have records order by some columns already and need to check order of both tables are same using GUID column)
If Guid matches then I will insert information in some table and if not then into log table, but it should move to/compare next record in both cases.
I am thinking to nested loop for both temp tables and check the order by comparing Guid columns.
Any other approach?
Your question is not very clear. Just some hard facts:
There is no implicit order! You can fill your table in a given order and the next SELECT might return the data exactly in this order - but this is random! You should never rely on a sort order! There is none!
The only guaranteed way to enforce a sort order is to use ORDER BY on the outermost query.
One specialty might be the usage of sorting functions like ROW_NUMBER(). But this is to broad to discuss this here.
If I get you correctly, you need to check for rows existing on both sides, if they appear in the same order. Try this:
DECLARE #t1 TABLE(YourGuid UNIQUEIDENTIFIER, Descr VARCHAR(100),SomeSortableColumn DATETIME);
INSERT INTO #t1 VALUES('653E6A93-3EBA-4D5E-A8F3-C36462A55FEF','Row 1',{d'2018-01-01'})
,('5461F417-1D14-4CFE-822D-3F028492F839','Row 2',{d'2018-01-02'})
,('E9BDE8C6-237A-49F6-88BD-9EB211FB12F2','Row 3',{d'2018-01-03'})
,('64343D33-8AD2-475F-AC27-66A6BFD011C9','Row 4',{d'2018-01-04'})
,('5778229D-B20E-41FC-9A2E-8694B204E4D3','Row 5',{d'2018-01-05'})
,('9AC0BB10-0F70-488C-A249-45A3C688D877','Row 6',{d'2018-01-06'})
,('330526D6-B931-4CEA-BB03-30F3783E6284','Row 7',{d'2018-01-07'})
,('6F68F260-2F64-4C78-9DA5-20E0FF22B4A1','Row 8',{d'2018-01-08'})
,('E09090F1-FC85-41EE-819B-8275A22BD075','Row 9',{d'2018-01-09'});
DECLARE #t2 TABLE(YourGuid UNIQUEIDENTIFIER, Descr VARCHAR(100),SomeSortableColumn DATETIME);
INSERT INTO #t2 VALUES('653E6A93-3EBA-4D5E-A8F3-C36462A55FEF','Row 1',{d'2018-01-01'})
,('5461F417-1D14-4CFE-822D-3F028492F839','Row 2',{d'2018-01-02'})
--missing in 2: 3 & 4
,('5778229D-B20E-41FC-9A2E-8694B204E4D3','Row 5',{d'2018-01-05'})
--other GUID
,(NEWID(),'Row 6',{d'2018-01-06'})
,('330526D6-B931-4CEA-BB03-30F3783E6284','Row 7',{d'2018-01-07'})
--other date
,('6F68F260-2F64-4C78-9DA5-20E0FF22B4A1','Row 8',{d'2018-01-01'})
,('E09090F1-FC85-41EE-819B-8275A22BD075','Row 9',{d'2018-01-09'})
--missing in 1
,(NEWID(),'Other row',{d'2018-01-03'})
;
--This query uses an INNER JOIN on the GUID column to omit rows, which do not exist in both sets. And it uses two times ROW_NUMBER(), each call sorted after the same column, but taken from different sources. The result shows rows where these indexes are different.
WITH ColumnsToCompare AS
(
SELECT t1.YourGuid
,t1.Descr AS Descr1
,t2.Descr AS Descr2
,t1.SomeSortableColumn AS Sort1
,t2.SomeSortableColumn AS Sort2
,ROW_NUMBER() OVER(ORDER BY t1.SomeSortableColumn) AS Index1
,ROW_NUMBER() OVER(ORDER BY t2.SomeSortableColumn) AS Index2
FROM #t1 AS t1
INNER JOIN #t2 AS t2 ON t1.YourGuid =t2.YourGuid
)
SELECT *
FROM ColumnsToCompare
WHERE Index1<>Index2
Not sure if I got your question right but below is what I think you should do.
The below is from your question
"If Guid matches then I will insert information in some table and if not then into log table, but it should move to/compare next record in both cases."
You need 2 insert statements
In the first one, do an inner join on GUIDs and insert the result to table1.
In the second query, do the left join and filter it by null and then insert the result set in to the log table.
insert into sometable
select *
from Table1 t1
inner join Table2 t2 on T1.GUID = T2.GUID
insert into logtable
select *
from Table1 t1
left join Table2 t2 on T1.GUID = T2.GUID
where t2.guid is null
You can generate row numbers (without ordering) and check for the GUIDs having different row numbers.
declare #table1 table(id varchar(MAX))
declare #table2 table(id VARCHAR(MAX))
insert into #table1
select '653E6A93'
union all
select '5461F417'
union all
select '330526D6'
insert into #table2
select '653E6A93'
union all
select '330526D6'
union all
select '5461F417'
;with cte1
AS
(
select *, ROW_NUMBER() OVER (ORDER BY (SELECT null)) AS rn from #table1
)
,
cte2
AS
(
select *, row_number() OVER(order by (SELECT NULL)) rn from #table2
)
select c1.id from cte1 c1
JOIN cte2 c2 on c1.id=c2.id and c1.rn<>c2.rn

Recursive Update Statement

I need to create a recursive update statement that updates from another table so for ex..
Table1
(
IdNumberGeneratedFromAService INT NOT NULL,
CodeName NVARCHAR(MAX)
)
Table2
(
Table2Id Auto_Increment,
Name NVARCHAR(MAX),
IdNumberThatComesFromTabl1,
CodeNameForTable1ToMatch
)
the issue is CodeNameForTable1ToMatch is not unique so if Table1 has 2 idnumber for the same code and there are two rows in Table2 with the same CodeName I want to update the rows in table2 in sequence so first row gets the first idnumber and second row gets the second id number.
Also want to do it without cursor....
SAMPLE DATA
Table1
idNumber Code
C145-6678-90 Code1
C145-6678-91 Code1
C145-6678-92 Code1
C145-6678-93 Code1
C145-6678-94 Code1
Table 2
AutoIncrementIdNumber Code IdNumber
1 Code1 {NULL}
2 Code1 {NULL}
3 Code1 {NULL}
4 Code1 {NULL}
5 Code1 {NULL}
C145-6678-90 needs to got 1
C145-6678-91 needs to got 2
C145-6678-92 needs to got 3
C145-6678-93 needs to got 4
C145-6678-94 needs to got 5
in one update statement
Using the ROW_NUMBER windowing function on each of the tables, partitioned by the code, you can number each of the rows that have a code in common, then combine the results of that on each query to match rows based on the code and the numbered instance of that code. So the first Code A in Table 1 would matched the first Code A in table 2, and etc.
Sample code showing this (SQL 2005 or higher):
-- Sample code prep
CREATE TABLE #Table1
(
IdNumberGeneratedFromAService INT NOT NULL,
CodeName NVARCHAR(MAX)
);
CREATE TABLE #Table2
(
Table2Id INT NOT NULL IDENTITY(1,1),
Name NVARCHAR(MAX),
IdNumberThatComesFromTabl1 INT NULL,
CodeNameForTable1ToMatch NVARCHAR(MAX)
);
INSERT INTO #Table1(IdNumberGeneratedFromAService, CodeName)
VALUES(100,'Code A'),(150,'Code A'),(200,'Code B'),(250,'Code A'),(300,'Code C'),(400,'Nonexistent');
INSERT INTO #Table2(Name, IdNumberThatComesFromTabl1, CodeNameForTable1ToMatch)
VALUES('A1-100',0,'Code A'),('A2-150',0,'Code A'),('A3-250',0,'Code A'),('B1-200',0,'Code B'),('C1-300',0,'Code C'),('No Id For Me',0,'Code No Id :(');
-- Sample select statement that shows the row numbers
--SELECT *
--FROM
-- (SELECT *, ROW_NUMBER() OVER (Partition By IT2.CodeNameForTable1ToMatch Order By IT2.Table2Id) as RowNum
-- FROM #Table2 IT2) T2
-- INNER JOIN
-- (SELECT *, ROW_NUMBER() OVER (Partition By IT1.CodeName Order By IT1.IdNumberGeneratedFromAService) as RowNum
-- FROM #Table1 IT1) T1
-- ON T1.CodeName = T2.CodeNameForTable1ToMatch AND T1.RowNum = T2.RowNum;
-- Table 2 Before
SELECT * FROM #Table2;
-- Actual update statement
UPDATE #Table2
SET IdNumberThatComesFromTabl1 = T1.IdNumberGeneratedFromAService
FROM #Table2 AT2
INNER JOIN
(SELECT *, ROW_NUMBER() OVER (Partition By IT2.CodeNameForTable1ToMatch Order By IT2.IdNumberThatComesFromTabl1) as RowNum
FROM #Table2 IT2) T2
ON T2.Table2Id = AT2.Table2Id
INNER JOIN
(SELECT *, ROW_NUMBER() OVER (Partition By IT1.CodeName Order By IT1.IdNumberGeneratedFromAService) as RowNum
FROM #Table1 IT1) T1
ON T1.CodeName = T2.CodeNameForTable1ToMatch AND T1.RowNum = T2.RowNum;
-- Table 2 after
SELECT * FROM #Table2;
-- Cleanup
DROP TABLE #Table1;
DROP TABLE #Table2;
I turned your two sample tables into temp tables and added 3 records for 'Code A', a record for 'Code B', and a record for 'Code C'. The codes in table1 are numbered based on the order of the table 1 ID, the codes in Table 2 are ordered by the auto-incrementing Table 2 id. I also included a record in each table that wouldn't have a match in the other. I tried to make the code's descriptive so it would be easier to see that a correct match has occurred (they order for table 2 is important since it has an auto incrementing id)
The commented out sample select is there to help understand how the select works before I join it into the UPDATE statement.
So we can see before the update Table 2 is all 0's, then we update the values in table 2 where the unique table 2 id matches the unique table 2 id from our nicely numbered and matched join, then we select from table 2 again to see the results.
A riff on Tarwn's solution:
with cte1 as (
select code, row_number() over (partition by code order by idNumber) as [rn]
from table1
), cte2 as (
select code, row_number() over (partition by code order by AutoIncrementIdNumber) as [rn]
from table2
)
update cte2
set idNumber = cte1.idNumber
from cte2
inner join cte1
on cte2.code = cte1.code
and cte2.rn = cte1.rn
I only present this because people are often amazed that you can update a common table expression.
This isn't possible without a cursor.