I am using sqlserver and I have two table which contains below data. I need to select those matched rows without duplicate.
Table_A:
A_ID | Item_ID
--------------------
1 | 101
2 | 101
3 | 103
4 | 103
5 | 199
Table_B:
B_ID | Item_ID
--------------------
11 | 101
12 | 101
13 | 102
14 | 103
15 | 103
16 | 103
Expected Result:
A_ID | Item_ID | B_ID
----------------------
1 | 101 | 11
2 | 101 | 12
3 | 103 | 14
4 | 103 | 15
I tried:
SELECT A_ID, a.Item_ID, B_ID FROM Table_A a LEFT JOIN
Table_B b ON a.Item_ID = b.Item_ID
But it show all the possible records.
How can i display the expected result above?
Based on the result set you gave you want one unique record from B for each A, ignoring records in A for which there is no corresponding record in B. The following will work:
SELECT
AValues.A_ID,
AValues.Item_ID,
BValues.B_ID
FROM
(SELECT
A_ID,
Item_ID,
ROW_NUMBER() OVER(PARTITION BY Item_ID ORDER BY A_ID) ARowID
FROM
Table_A) AValues
INNER JOIN (SELECT
B_ID,
Item_ID,
ROW_NUMBER() OVER(PARTITION BY Item_ID ORDER BY B_ID) BRowID
FROM
Table_B) BValues ON AValues.Item_ID = BValues.Item_ID AND AValues.ARowID = BValues.BRowID
Related
Table1 (orgumcae)
caseid | auth_status_summary | delegate_system
-------+---------------------+----------------
100 | 1 | 14
200 | 2 | 13
SQL:
create table orgumcae(caseid int, auth_status_summary int, delegate_system int);
insert into orgumcae values(100, 1, 14);
insert into orgumcae values(200, 2, 13);
Table2 (stringlocale)
id | subid | string
----+-------+--------
148 | 1 | AAA
148 | 2 | BBB
148 | 3 | CCC
148 | 14 | DDD
152 | 11 | WWW
152 | 12 | XXX
152 | 13 | YYY
152 | 14 | ZZZ
SQL:
create table stringlocale(id int, subid int, string varchar(40));
insert into stringlocale values(148,1,'AAA');
insert into stringlocale values(148,2,'BBB');
insert into stringlocale values(148,14,'DDD');
insert into stringlocale values(152,11,'WWW');
insert into stringlocale values(152,13,'YYY');
insert into stringlocale values(152,14,'ZZZ');
Resultset
caseid | auth_status_summary |auth_status_summary_string | delegate_system | delegate_system_string
-------+---------------------+---------------------------+-----------------+-----------------------
100 | 1 | AAA | 14 | ZZZ
200 | 2 | BBB | 13 | YYY
There are 2 tables Table1 and Table2 and a Resultset. Can I get some help to achieve the Resultset without using temp tables?
Here auth_status_summary column of Table1 denotes 148 value for column id of Table2 and delegate_system column of Table1 denotes 152 value for column id of Table2, so for auth_status_summary use where clause table2.id = 148 for delegate_system use where clause table2.id = 152
Select case_id, t1.auth_status_summary, t2.string, t1.delegate_system, t22.string
from table1 as t1
inner join table2 as t2 on t1.auth_status_summary = t2.subid
inner join table2 as t22 on t1.delegate_system = t22.subid
This should do:
select t1.caseid,
t1.auth_status_summary,
t2_auth.string as auth_status_summary_string,
t1.delegate_system,
t2_deleg.string as delegate_system_string
from orgumcae t1
left outer join stringlocale t2_auth
on t2_auth.id = 148
and t2_auth.subid = t1.auth_status_summary
left outer join stringlocale t2_deleg
on t2_deleg.id = 152
and t2_deleg.subid = t1.delegate_system
order by 1
The key part is to use the Table2 (stringlocale) twice, with different aliases.
See this db<>fiddle
How do I create multiple rows in a new table based on a common value in another table?
ProviderTable: PersonTable:
-------------------- ---------------------
ProviderID | GroupID PersonID | ProviderID
1 | A 100 | 1
2 | A 101 | 3
3 | A 102 | 8
4 | NULL 103 | 10
5 | B 104 | 5
6 | C 105 | 4
7 | B
8 | NULL
9 | NULL
10 | C
ProviderTable.ProviderID = PersonTable.ProviderID
I need to create a new table with a person row for each provider where Provider.GroupID=Provider.GroupID
Results I am looking for:
New-table:
PersonID | ProviderID
100 | 1
100 | 2
100 | 3
101 | 3
101 | 1
101 | 2
102 | 8
103 | 10
103 | 6
104 | 5
104 | 7
105 | 4
This quick version gets the sort order you are looking for. Test data is included:
DECLARE #pt table (
ProviderId int,
GroupId varchar(2)
)
DECLARE #pet table (
PersonId int,
ProviderId int
)
INSERT INTO #pt Values
(1,'A'),
(2,'A'),
(3,'A'),
(4,NULL),
(5,'B'),
(6,'C'),
(7,'B'),
(8,NULL),
(9,NULL),
(10,'C')
INSERT INTO #pet VALUES
(100,1),
(101,3),
(102,8),
(103,10),
(104,5),
(105,4)
SELECT pe.PersonId,
IsNull(p2.ProviderId, p1.providerId) As ProviderId
FROM #pt p1
INNER JOIN #pet pe
ON p1.ProviderId = pe.ProviderId
LEFT JOIN #pt p2
ON p1.GroupId = p2.GroupId
ORDER BY pe.personId,
CASE
WHEN pe.ProviderId = p2.ProviderId
Then 0
ELSE 1
END
You can use the following SQL-statement:
SELECT PER.PersonID, COALESCE(PG.ProviderID, PP.ProviderID) AS ProviederID
FROM PersonTable PER
INNER JOIN ProviderTable PP
ON PP.ProviderID = PER.ProviderID
LEFT OUTER JOIN ProviderTable PG
ON PG.GroupID = PP.GroupID;
I have a table with a million records. I need to update some columns which are null based on the existing 'not null' records of a particular id based columns. I've tried with one query, it seems to be working fine but I don't have confidence in it that it will be able to update all those 1 million records exactly the way I need. I'm providing you some sample data how my table looks like.Any help will be appreciated
SELECT * INTO #TEST FROM (
SELECT 1 AS EMP_ID,10 AS DEPT_ID,15 AS ITEM_NBR ,NULL AS AMOUNT,NULL AS ITEM_NME
UNION ALL
SELECT 1,20,16,500,'ABCD'
UNION ALL
SELECT 1,30,17,NULL,NULL
UNION ALL
SELECT 2,10,15,1000,'XYZ'
UNION ALL
SELECT 2,30,16,NULL,NULL
UNION ALL
SELECT 2,40,17,NULL,NULL
) AS A
Sample data:
+--------+---------+----------+--------+----------+
| EMP_ID | DEPT_ID | ITEM_NBR | AMOUNT | ITEM_NME |
+--------+---------+----------+--------+----------+
| 1 | 10 | 15 | NULL | NULL |
| 1 | 20 | 16 | 500 | ABCD |
| 1 | 30 | 17 | NULL | NULL |
| 2 | 10 | 15 | 1000 | XYZ |
| 2 | 30 | 16 | NULL | NULL |
| 2 | 40 | 17 | NULL | NULL |
+--------+---------+----------+--------+----------+
Expected result:
+--------+---------+----------+--------+----------+
| EMP_ID | DEPT_ID | ITEM_NBR | AMOUNT | ITEM_NME |
+--------+---------+----------+--------+----------+
| 1 | 10 | 15 | 500 | ABCD |
| 1 | 20 | 16 | 500 | ABCD |
| 1 | 30 | 17 | 500 | ABCD |
| 2 | 10 | 15 | 1000 | XYZ |
| 2 | 30 | 16 | 1000 | XYZ |
| 2 | 40 | 17 | 1000 | XYZ |
+--------+---------+----------+--------+----------+
I tried this but I'm unable to conclude whether it is updating all the 1 million records properly.
SELECT * FROM #TEST T
inner JOIN #TEST T1 ON T1.EMP_ID=T.EMP_ID
WHERE T1.AMOUNT IS NOT NULL
UPDATE T SET AMOUNT=T1.AMOUNT
FROM #TEST T
inner JOIN #TEST T1 ON T1.EMP_ID=T.EMP_ID
WHERE T1.AMOUNT IS not NULL
I have used UPDATE using inner join
UPDATE T
SET T.AMOUNT = X.AMT,T.ITEM_NME=X.I_N
FROM #TEST T
JOIN
(SELECT EMP_ID,MAX(AMOUNT) AS AMT,MAX(ITEM_NME) AS I_N
FROM #TEST
GROUP BY EMP_ID) X ON X.EMP_ID = T.EMP_ID
SELECT * into #Test1
FROM #TEST
WHERE AMOUNT IS NOT NULL
For records validation run this query first
SELECT T.AMOUNT, T1.AMOUNT, T1.EMP_ID,T1.EMP_ID
FROM #TEST T
inner JOIN #TEST1 T1 ON T1.EMP_ID=T.EMP_ID
WHERE T.AMOUNT IS NULL
Begin Trans
UPDATE T
SET T.AMOUNT=T1.AMOUNT, T.ITEM_NME= = T1.ITEM_NME
FROM #TEST T
inner JOIN #TEST1 T1 ON T1.EMP_ID=T.EMP_ID
WHERE T.AMOUNT IS NULL
rollback
SELECT EMP_ID,MAX(AMOUNT) as AMOUNT MAX(ITEM_NAME) as ITEM_NAME
INTO #t
FROM #TEST
GROUP BY EMP_ID
UPDATE t SET t.AMOUNT = t1.AMOUNT, t.ITEM_NAME = t1.ITEM_NAME
FROM #TEST t INNER JOIN #t t1
ON t.emp_id = t1.emp_id
WHERE t.AMOUNT IS NULL and t.ITEM_NAME IS NULL
Use MAX aggregate function to get amount and item name for each employee and then replace null values of amount and item name with those values. For validation use COUNT function to calculate the number of rows with values of amount and item name as null. If the number of rows is zero then table is updated correctly
I have the Following tables
Disposition Table
Dis_ID | OfferID | RequestID
------------------------------------
34564 | 123 | 9
77456 | 123 | 8
25252 | 124 | 7
46464 | 125 | 10
36464 | 125 | 6
35353 | 125 | 5
Request Table
RequestID | AccountNum |
---------------------------
5 | 548543 |
6 | 548543 |
7 | 684567 |
8 | 684567 |
9 | 684567 |
10 | 548543 |
11 | 684567 |
Rank Table
RankID | OfferId | RequestID | Score
-------------------------------------------
34564 | 123 | 11 | 1
77456 | 124 | 11 | 2
25252 | 125 | 11 | 3
Using the data above I need a query which would behave as follows given a request number look at every record in the Rank Table in this example we have 3 (123, 124, & 125). return the OfferId that appears the fewest times in the Disposition table for this joined account number. in this example offerId 123 appears twice for this account number, offerId 124 appears once and offerId 125 doesn't appears at all for this account number. So offerId 125 should be returned. The offerId which exist in the Rank Table with the fewest appearances in the Disposition table should always be returned unless they are all the same then return the offerId with the lowest value in the Score field. for example if none of the offerIDs appeared in the Dispostion table offerId 123 would return since its Score value is 1.
Resulting table would look something like this
| OfferId | Score | Dis_Occurrences
---------------------------------------------------------------
| 123 | 1 | 2
| 124 | 2 | 1
| 125 | 3 | 0 <--Return this record
This is what I have so far.
SELECT oRank.OfferId, oRank.Rank_Number, count(oRank.OfferId) AS NumDispositions
From Rank oRank
join Request req
on oRank.RequestId = req.RequestId
join Disposition dis
on oRank.OfferId = dis.OfferId
where req.Customer_Account_Number = 684567 and req.RequestId = 11 and oRank.OfferId = dis.OfferId
group by oRank.Rank_Number, oRank.OfferId
order by NumDispositions, oRank.Rank_Number
My incorrect Resulting table looks like this
| OfferId | Score | Dis_Occurrences
---------------------------------------------------------------
| 123 | 1 | 2
| 124 | 2 | 1
| 125 | 3 | 3
It is counting the total number of times the offerId appears in the Disposition Table
EDIT - based on author's comments, here's another version:
Example in SQLFiddle: http://sqlfiddle.com/#!6/d3f99/1/0
with RankReqMap as (
select rnk.OfferId, rnk.Score, reqAcct.AccountNum, reqReq.RequestID
from [Rank] rnk
left join Request reqAcct on reqAcct.RequestID = rnk.RequestID
left join Request reqReq on reqReq.AccountNum = reqAcct.AccountNum
where rnk.RequestID = 11 -- Put your RequestId filter here
)
select oRank.OfferId
,oRank.Score
,count(dis.RequestID) as NumDispositions
from RankReqMap oRank
left join Disposition dis on dis.OfferID = oRank.OfferId
and dis.RequestID = oRank.RequestID
group by oRank.OfferId , oRank.Score
order by NumDispositions, oRank.Score;
ORIGINAL POST
Example in SQLFiddle: http://sqlfiddle.com/#!6/770a8/1/0
This query makes the assumption that you're joining Disposition to Rank based on OfferID, since the RequestIDs for those tables in your example data don't match up. You may have to tweak depending on your needs, but something like the query below should get you the record you're looking for:
-- Gather base data
with RankData as (
select rnk.RankID
,rnk.OfferID
,rnk.RequestID
,rnk.Score
,Dis_Occurrences = count(dis.OfferID)
from dbo.[Rank] rnk
left join dbo.Disposition dis on dis.OfferID = rnk.OfferId
left join dbo.Request req on req.RequestID = rnk.RequestID
group by rnk.RankID, rnk.OfferID, rnk.RequestID, rnk.Score
)
-- Rank count of Dis_Occurrences, taking lowest score into account as a tie breaker
, DispRanking as (
select rdt.*, Dis_Rank = row_number() over (order by Dis_Occurrences asc, rdt.Score asc)
from RankData rdt
)
-- Return only the value with the highest ranking
select * from DispRanking where Dis_Rank = 1
Note also that if you convert the second CTE into a naked SELECT and remove the SELECT statement at the end, you can see all of the records and how they get ranked by the row_number() function:
-- Gather base data
with RankData as (
select rnk.RankID
,rnk.OfferID
,rnk.RequestID
,rnk.Score
,Dis_Occurrences = count(dis.OfferID)
from dbo.[Rank] rnk
left join dbo.Disposition dis on dis.OfferID = rnk.OfferId
left join dbo.Request req on req.RequestID = rnk.RequestID
group by rnk.RankID, rnk.OfferID, rnk.RequestID, rnk.Score
)
-- Output all values, with rankings
select rdt.*, Dis_Rank = row_number() over (order by Dis_Occurrences asc, rdt.Score asc)
from RankData rdt
Good luck!
I think you can use window function for this:
;with disp as(select offerid, count(*) as ocount
from dispositions group by offerid),
rnk as(select r.offerid,
row_number() over(partition by r.requestid
order by isnull(d.ocount, 0), r.score) rn
from ranks r
left join disp d on r.offerid = d.offerid)
select * from rnk where rn = 1
we have following table (output is already ordered and separated for understanding):
| PK | FK1 | FK2 | ActionCode | CreationTS | SomeAttributeValue |
+----+-----+-----+--------------+---------------------+--------------------+
| 6 | 100 | 500 | Create | 2011-01-02 00:00:00 | H |
----------------------------------------------------------------------------
| 3 | 100 | 500 | Change | 2011-01-01 02:00:00 | Z |
| 2 | 100 | 500 | Change | 2011-01-01 01:00:00 | X |
| 1 | 100 | 500 | Create | 2011-01-01 00:00:00 | Y |
----------------------------------------------------------------------------
| 4 | 100 | 510 | Create | 2011-01-01 00:30:00 | T |
----------------------------------------------------------------------------
| 5 | 100 | 520 | CreateSystem | 2011-01-01 00:30:00 | A |
----------------------------------------------------------------------------
what is ActionCode? we use this in c# and there it represents an enum-value
what do i want to achieve?
well, i need the following output:
| FK1 | FK2 | ActionCode | SomeAttributeValue |
+-----+-----+--------------+--------------------+
| 100 | 500 | Create | H |
| 100 | 500 | Create | Z |
| 100 | 510 | Create | T |
| 100 | 520 | CreateSystem | A |
-------------------------------------------------
well, what is the actual logic?
we have some logical groups for composite-key (FK1 + FK2). each of these groups can be broken into partitions, which begin with Create or CreateSystem. each partition ends with Create, CreateSystem or Change. The actual value of SomeAttributeValue for each partition should be the value from the last line of the partition.
it is not possible to have following datapool:
| PK | FK1 | FK2 | ActionCode | CreationTS | SomeAttributeValue |
+----+-----+-----+--------------+---------------------+--------------------+
| 7 | 100 | 500 | Change | 2011-01-02 02:00:00 | Z |
| 6 | 100 | 500 | Create | 2011-01-02 00:00:00 | H |
| 2 | 100 | 500 | Change | 2011-01-01 01:00:00 | X |
| 1 | 100 | 500 | Create | 2011-01-01 00:00:00 | Y |
----------------------------------------------------------------------------
and then expect PK 7 to affect PK 2 or PK 6 to affect PK 1.
i don't even know how/where to start ... how can i achieve this?
we are running on mssql 2005+
EDIT:
there's a dump available:
instanceId: my PK
tenantId: FK 1
campaignId: FK 2
callId: FK 3
refillCounter: FK 4
ticketType: ActionCode (1 & 4 & 6 are Create, 5 is Change, 3 must be ignored)
ticketType, profileId, contactPersonId, ownerId, handlingStartTime, handlingEndTime, memo, callWasPreselected, creatorId, creationTS, changerId, changeTS should be taken from the Create (first line in partition in groups)
callingState, reasonId, followUpDate, callingAttempts and callingAttemptsConsecutivelyNotReached should be taken from the last Create (which then would be a "one-line-partition-in-group" / the same as the upper one) or Change (last line in partition in groups)
I'm assuming that each partition can only contain a single Create or CreateSystem, otherwise your requirements are ill-defined. The following is untested, since I don't have a sample table, nor sample data in an easily consumed format:
;With Partitions as (
Select
t1.FK1,
t1.FK2,
t1.CreationTS as StartTS,
t2.CreationTS as EndTS
From
Table t1
left join
Table t2
on
t1.FK1 = t2.FK1 and
t1.FK2 = t2.FK2 and
t1.CreationTS < t2.CreationTS and
t2.ActionCode in ('Create','CreateSystem')
left join
Table t3
on
t1.FK1 = t3.FK1 and
t1.FK2 = t3.FK2 and
t1.CreationTS < t3.CreationTS and
t3.CreationTS < t2.CreationTS and
t3.ActionCode in ('Create','CreateSystem')
where
t1.ActionCode in ('Create','CreateSystem') and
t3.FK1 is null
), PartitionRows as (
SELECT
t1.FK1,
t1.FK2,
t1.ActionCode,
t2.SomeAttributeValue,
ROW_NUMBER() OVER (PARTITION_FRAGMENT_ID BY t1.FK1,T1.FK2,t1.StartTS ORDER BY t2.CreationTS desc) as rn
from
Partitions t1
inner join
Table t2
on
t1.FK1 = t2.FK1 and
t1.FK2 = t2.FK2 and
t1.StartTS <= t2.CreationTS and
(t2.CreationTS < t1.EndTS or t1.EndTS is null)
)
select * from PartitionRows where rn = 1
(Please note than I'm using all kinds of reserved names here)
The basic logic is: The Partitions CTE is used to define each partition in terms of the FK1, FK2, an inclusive start timestamp, and exclusive end timestamp. It does this by a triple join to the base table. the rows from t2 are selected to occur after the rows from t1, then the rows from t3 are selected to occur between the matching rows from t1 and t2. Then, in the WHERE clause, we exclude any rows from the result set where a match occurred from t3 - the result being that the row from t1 and the row from t2 represent the start of two adjacent partitions.
The second CTE then retrieves all rows from Table for each partition, but assigning a ROW_NUMBER() score within each partition, based on the CreationTS, sorted descending, with the result that ROW_NUMBER() 1 within each partition is the last row to occur.
Finally, within the select, we choose those rows that occur last within their respective partitions.
This does all assume that CreationTS values are distinct within each partition. I may be able to re-work it using PK also, if that assumption doesn't hold up.
It is solvable with a recursive CTE. Here (assuming rows within partitions are ordered by CreationTS):
WITH partitioned AS (
SELECT
*,
rn = ROW_NUMBER() OVER (PARTITION BY FK1, FK2 ORDER BY CreationTS)
FROM data
),
subgroups AS (
SELECT
PK, FK1, FK2, ActionCode, CreationTS, SomeAttributeValue, rn,
Subgroup = 1,
Subrank = 1
FROM partitioned
WHERE rn = 1
UNION ALL
SELECT
p.PK, p.FK1, p.FK2, p.ActionCode, p.CreationTS, p.SomeAttributeValue, p.rn,
Subgroup = s.Subgroup + CASE p.ActionCode WHEN 'Change' THEN 0 ELSE 1 END,
Subrank = CASE p.ActionCode WHEN 'Change' THEN s.Subrank ELSE 0 END + 1
FROM partitioned p
INNER JOIN subgroups s ON p.FK1 = s.FK1 AND p.FK2 = s.FK2
AND p.rn = s.rn + 1
),
finalranks AS (
SELECT
PK, FK1, FK2, ActionCode, CreationTS, SomeAttributeValue, rn,
Subgroup, Subrank,
rank = ROW_NUMBER() OVER (PARTITION BY FK1, FK2, Subgroup ORDER BY Subrank DESC)
/* or: rank = MAX(Subrank) OVER (PARTITION BY FK1, FK2, Subgroup) - Subrank + 1 */
FROM subgroups
)
SELECT PK, FK1, FK2, ActionCode, CreationTS, SomeAttributeValue
FROM finalranks
WHERE rank = 1