SQL Server cross-table join - sql

For each AVI there can have multiple AAIs. What I want to do is find the first non-null UserID for each AVI sorted by AAI in ascending order. I then want to get the username of that user id from the user table.
So in the example below, AVI 165 would return William. i.e. it would have sorted by AAI, ignored the userid associated with 415 as this is null, returned userid 58 (corresponding to AAI416) and joined this to the user table to get William.
Table: IDS
AAI AVI UserId
------------------
415 165 NULL
416 165 58
417 165 67
210 510 71
211 510 NULL
433 534 262
Table: Username
UserId UserName
----------------
1 John
58 William
33 Lucy
45 Haley
51 Rob
I've tried it with various complex lookups, and thought I had it with this fairly simple query but it doesn't cater for the null values i.e. returns null where the first AAI has a null user value.
select
u.username
from
(select
avi, min(aai) as aaid
from
IDS
group by
avi) as au
inner join
IDS as aa on aa.aai = au.aaid
inner join
"user" as u on u.userid = aa.userid
I also tried various sub-queries along these lines but they returned all results for each AAI, not just the first.
select ui.username, aa.avi
from "user" as ui
join dbo.IDS as aa
on aa.userid=
(select top 1 userid
from dbo.IDS as aa
where aa.userid = ui.userid
)
Any pointer/help would be much appreciated.

I think you just want a way to get the first. row_number() does this:
select t.*, un.*
from (select i.*,
row_number() over (partition by avi order by aai) as seqnum
from ids i
where userid is not null
) i join
username un
on i.userid = un.userid
where seqnum = 1;

Is something like this what you are looking for?
CREATE TABLE #IDS (AAI INT, AVI INT, UserID INT)
INSERT INTO #IDS VALUES (415,165,NULL),(416,165,58),(417,165,67),(210,510,71)
CREATE TABLE #Username (UserId INT, UserName VARCHAR(20))
INSERT INTO #Username VALUES (1,'JOHN'),(58,'William'), (71,'Lucy')
SELECT #Username.username,
ca.AAI,
ca.AVI,
ca.UserID
FROM #Username
CROSS APPLY
(
SELECT TOP 1
AAI,
AVI,
UserID
FROM #IDS
WHERE #IDS.UserID = #Username.UserID
ORDER BY #IDS.AAI ASC
) AS ca;

You could use a subquery to get the first non null userid per avi sorted by aai, and then join those userids to the username table. Something along these lines..
select i.avi, un.username
from username un
inner join
(select distinct avi, min(userid) over (partition by avi order by aai) userid from ids) i
on i.userid=un.userid and un.username is not null;
If duplicated data is not a concern, you could also do
select i.avi, un.username
from username un
inner join ids i on i.userid=un.userid and un.username is not null
where i.userid in (select min(userid) over (partition by avi order by aai) from ids)

You are looking for a groupwise minimum. See this post from MySQL for an explanation of groupwise maximum:
https://dev.mysql.com/doc/refman/8.0/en/example-maximum-column-group-row.html

Related

Duplicates in SQL table: How to map the duplicates based on the rows value?

I have a table that looks like follwing (In microsoft sql server):
[ID]
,[DamageCodeGroupID]
,[Name]
,[Swedish]
,[Finnish]
,[CreatedDate]
,[CreatedBy]
,[UpdatedDate]
,[UpdatedBy]
[dbo].[DamageCode]
This table has many rows, and some of them have the same value. For example, ID 98 has the Swedish value 'Motor', so does ID number 286. How can I map this with some sort of sql query to see which rows have duplicates? (Based on the column Swedish)
To clarify, for example when doing:
SELECT * FROM DamageCode WHERE Swedish = 'motor'
The result is:
ID DamageCodeGroupID Name Swedish Finnish
98 17 ENG01 Motor Moottori
286 41 Motor Motor
So the result i want is some sort of script where i can map these older DamageCodes with the new ones. I have older Damage codes from ID 1 to 138. From ID 139 to 339 i have new codes. The output i want is to see how these are related, so for example if id 77 have the same name as id 133 or something similar.
…
select
[ID]
,[DamageCodeGroupID]
,[Name]
,[Swedish]
,[Finnish]
,[CreatedDate]
,[CreatedBy]
,[UpdatedDate]
,[UpdatedBy]
from
(
select *,
-- If ID is unique
min(ID) over(partition by [Swedish]) as minID,
max(ID) over(partition by [Swedish]) as maxID
--count(*) over(partition by [Swedish]) as cnt
from [dbo].[DamageCode]
) as dc
where minID <> maxID -- cnt >= 2
order by [Swedish], [ID];
WITH CTE AS
(
SELECT 1 AS ID,'MOTOR'AS VALUE
UNION ALL
SELECT 2 AS ID,'ENGINE'AS VALUE
UNION ALL
SELECT 3 AS ID,'MOTOR'AS VALUE
)
SELECT STRING_AGG(C.ID,',') AS XX ,C.VALUE
FROM CTE AS C
GROUP BY C.VALUE
As you haven't provided sample data and desired output,
If you want to find all the rows that have the same value for a particular column eg swedish you can do something like the following
select * from (
select Swedish, Count(*) dupes
from DamageCode
group by Swedish
having Count(*)>1
)d
join DamageCode dc on dc.Swedish = d.Swedish
order by dc.Id

verify flag for user as parameter

I have table users
Userid flag
145 1
142 0
a second table Sales
salesId date amount
252 01/01/2021 125
23 01/02/2021 300
I need to ckeck each time for user parameter the flag to display or not the data
fOr the user 145 I'll display everything in the table sales because flag=1
but the user 142 I'll disply nothing beacuse flag =0
CREATE PROCEDURE Sales (#userID nvarchar(30))
AS
SELECT * FROM sales
cross join users
WHERE UserID= #userID and flag =1
GO;
The result is incorrect, how to correct it
Your version would work with select sales.*. However, I would use:
select s.*
from sales s
where exists (select 1
from users u
where u.UserId = #UserId and u.flag = 1
);
This makes it clear that users is just being used for filtering and that there really isn't a relationship between the two tables (other than the permissioning, of course).

How username from blocking session id

I am trying to write a query that will login name associated with a block session id if there is a blocking id >0
For instance, when I use the query on this table below:
login_name session_id blocking_session_id
Billy 50 0
Benjamin 60 70
John 70 0
I want the query to output
login_name session_id
John 70
Thanks for your help
You can do this in a single query by joining the same table twice on t1.session_id = t2.blocking_session_id (no need for a SELECT inside a SELECT) and selecting the desired fields from t1:
working example: http://www.sqlfiddle.com/#!9/685824/2
select t1.login_name, t1.session_id
from mytable as t1
join mytable as t2
on t1.session_id = t2.blocking_session_id
As I understand your question, you want the login names that own blocking sessions.
You can use exists for this:
select t.*
from mytable t
where exists (select 1 from mytable t1 where t1.blocking_session_id = t.session_id)

Sub Query having group by and count

tbl_Offer
OFID bigint
Offer_Text text
OFID Offer_Text
------- ----------
1014 Test1
1015 Test2
tbl_TransactionDishout
offerNo TerminalID Created
---------------------------------
1014 170924690436418 2010-05-25 12:51:59.547
tblVTSettings
gid mid tid
-----------------------
50 153 119600317313328
104 158 160064024922223
76 162 256674529511898
1111 148 123909123909123
These are the three tables.
Now I want the information of all deals (offers) separated by schools (look gid where TerminalID in (50,76,104)).
These are the three schools: (50,76,104)
The o/p should have these fields:
OfferID(OFID), School the offer is for, Offer_Text, Number of time the offer is.
The query may be somehow like this:
SELECT OFID, Offer_Text,
Counter =
(
SELECT COUNT(*) FROM dbo.tbl_TransactionDishout t
WHERE t.OfferNo = CAST(OFID AS NVARCHAR(30))
and t.TerminalID in
(select TID from tblVTSettings where gid in (50,76,104))
)
FROM dbo.tbl_Offer
Where EXISTS (SELECT * FROM dbo.tbl_TransactionDishout
WHERE OfferNo = CAST(OFID AS NVARCHAR(30)))
Please try this.
SELECT to.OFID
,ts.gid AS 'School the offer is for'
,to.Offer_Text
,COUNT(to.OFID) AS 'Number of time the offer is'
FROM tbl_Offer to
JOIN tbl_TransactionDishout tt
ON to.OFID = tt.offerNo
JOIN tblVTSettings ts
ON ts.tid = tt.TerminalID
Try:
SELECT o.OFID,
s.gid,
o.Offer_Text,
count(*) over (partition by o.OFID) number_schools,
count(*) over (partition by s.gid) number_offers
FROM tbl_Offer o
JOIN tbl_TransactionDishout d ON o.OFID = d.offerNo
JOIN tblVTSettings s ON s.tid = d.TerminalID

How to query two same values in SQL Server 2008?

I have table in SQL Server 2008 with following fields
RoomUserId->Primary key
RoomId->Foreign Key of Table Rooms
UserId->Foreign Key of Table Users
Now I have the values as following where the RoomId is common for both users
RoomUserId RoomId UserId
1 11 1
2 11 2
3 12 1
4 12 3
5 13 1
6 13 4
Now I need a SQL query to find the roomid of two users which is distinct. i.e, roomid of user 1 and user 2, roomid of user 1 and user 3.
Please anyone help me with is since I am new to SQL Server.
Try this:
SELECT r1.roomId FROM room r1 JOIN room r2 ON r1.roomId = r2.roomId WHERE r1.userId = 1 AND r2.userId = 3
If you mean find a roomid that is common to users:
select distinct f.roomid
from rooms f
inner join rooms s on f.roomid = s.roomid and f.userid <> s.userid
Or you can use grouping:
select roomid
from rooms
group by roomid
having count(distinct userid) > 1
If you only ever need rooms where there is more than one user then this will work:
SELECT DISTINCT RoomID
FROM RoomUser r1
INNER JOIN RoomUser r2
ON r1.RoomID = r2.RoomID
AND r1.RoomUserID != r2.RoomUserID
If you need the room ID of rooms with x users then use the Having Clause, this is more extensible than self joining e.g. if you need to find room IDs with 3 or more User IDs then you would end up with:
SELECT RoomID
FROM RoomUser
GROUP BY RoomID
HAVING COUNT(DISTINCT UserID) > 3
Whereas using self joins, while probably more efficient will end up with some quite messy SQL. Check execution plans and run some tests to see which is more efficient for your needs.
If you actually need the user IDs of the Users in Rooms with more than one User ID then you could use a CTE to build a comma separated string of users in each room:
;WITH RoomUserCTE AS
( SELECT RoomID,
MIN(UserID) [UserID],
CONVERT(VARCHAR(20), MIN(UserID)) [Users],
0 [Recursion]
FROM RoomUser
GROUP BY RoomID
UNION ALL
SELECT a.RoomID,
b.UserID [UserID],
CONVERT(VARCHAR(20), Users + ', ' + CONVERT(VARCHAR, b.UserID)),
Recursion + 1
FROM RoomUserCTE a
INNER JOIN RoomUser b
ON a.RoomID = b.RoomID
AND b.UserID > a.UserID
)
SELECT RoomID, Users
FROM ( SELECT *, MAX(Recursion) OVER(PARTITION BY RoomID) [MaxRecursion]
FROM RoomUserCTE
) cte
WHERE MaxRecursion = Recursion
For the data in your question this will yield
| RoomID | Users |
|---------+---------|
| 11 | 1, 2 |
| 12 | 1, 3 |
| 13 | 1, 4 |
This would work no matter how many user IDs were associated with the same Room ID, so again is more forward compatible.