SQL Query to fetch a user without an associated service - sql

I have the below data in a table. I am trying to fetch all the "Modem" users, who do not have an associated telephone service.
UserID DeviceNumber DeviceType DeviceRole
1 A Telephone SingleUser
1 A Modem MultiUser
1 B Modem MultiUser
2 C Telephone SingleUser
2 C Modem MultiUser
2 D Modem MultiUser
select distinct t.* from table t
join table t1 on t1.UserID= v.UserID
and t1.DeviceNumber <> t.DeviceNumber
and t.DeviceType = 'Modem';
I want to see DeviceNumber B and D in my output. But above query is not returning expected results.

Hmmm . . . One method would be:
select t.*
from t
where t.devicetype = 'Modem' and
not exists (select 1
from t t2
where t2.userid = t.userid and t2.devicenumber = t.devicenumber and
t2.devicetype = 'Telephone'
);

You can do by count. Here is the demo.
select
UserID,
DeviceNumber,
DeviceType,
DeviceRole
from
(
select
yt.*,
count(*) over (partition by DeviceNumber) as cnt
from yourTable yt
) val
where cnt = 1
and DeviceType = 'Modem'
Output:

Related

Update an Oracle table using another ungrouped table

The scenario involves two tables with the following structures :
>table e1:
Id Name Addr
---------------------
1 Ramas
2 Sam
3 Musa
4 Sebi
>table e1_addr:
Id Addr Mod_Date
-----------------------
1 Gzb 10-05-2018
1 Dli 18-05-2018
2 Gzb 25-05-2018
2 Dli 22-05-2018
3 Gzb 09-05-2018
3 Dli 05-05-2018
4 Gzb 14-05-2018
4 Dli 24-05-2018
table e1 has 4 personnel with no updated addresses in address column.
table e_addr has updated address record for each id with mod_date column having the date the record was entered.
I need to update table e1 so that its addr column has the value from the e1_addr table with maximum mod_date.
On my part, I have tried to fetch addr from e1_addr table corresponding to the max(mod_date) but cannot achieve the desired result.
Desired output is as follows :
Final Table :
Id Name Addr
------------------
1 Ramas Dli
2 Sam Gzb
3 Musa Gzb
4 Sebi Dli
Any and all help would be apprecited.
Please inform if i should give in some more details.
You can do this with keep:
update e1
set addr = (select max(a.addr) keep (dense_rank first order by mod_date desc)
from ei_addr a
where e1.id = a.id
);
I would suggest that you not store the address in two places and simply fetch the latest address when you query the e1 table.
You can try this query to update Addr column.
windows function with ROW_NUMBER to get Rownumber is 1 on e1_addr,which mean MAX Mod_Date.
then update e1.addr.
UPDATE
(
SELECT t.addr as OLD,t2.addr as NEW
FROM e1 t
INNER JOIN
(
SELECT t2.*,ROW_NUMBER() OVER(PARTITION BY t2.ID ORDER BY t2.Mod_Date desc) rn
FROM e1_addr t2
) t2 on t.id = t2.id
where t2.rn = 1
) t
SET t.OLD = t.NEW
or another update query you can try subquery.
UPDATE e1 t1
SET t1.addr = (SELECT t2.addr
FROM
(
SELECT t2.*,ROW_NUMBER() OVER(PARTITION BY t2.ID ORDER BY t2.Mod_Date desc) rn
FROM e1_addr t2
) t2
WHERE t2.rn = 1 and t1.id = t2.id
)
sqlfiddle:http://sqlfiddle.com/#!4/e14f5/1
Couldn't resist, the MERGE syntax wasn't mentioned yet:
MERGE INTO e1 USING (SELECT ID, MAX(addr) KEEP (DENSE_RANK FIRST ORDER BY mod_date DESC) AS newest_addr
FROM e1_addr GROUP BY ID) q ON (e1.ID = q.ID)
WHEN MATCHED THEN UPDATE SET e1.addr = q.newest_addr;
This subquery selects the newest address for each id:
SELECT ID, MAX(addr) KEEP (DENSE_RANK FIRST ORDER BY mod_date DESC) AS newest_addr
FROM e1_addr GROUP BY ID;
ID newest_addr
1 Dli
2 Gzb
3 Gzb
4 Dli
... and MERGE looks up each id in the subquery, and if it finds a match, it updates the addr.
Some people love it, some hate it.
Try this out:
Step1: Take id and max date from address table
Step 2: Join on id and max date with the address table to get the address
Step 3 Join this table with my e1 table to get the final output
select p.*,q.addr
from
e1 p
left join
(
select a.id,b.addr
from
(select id,max(mod_date) as updated_date
from e1_addr
group by id ) a
left join
e1_addr b
on a.id = b.id and a.updated_date = b.mod_date
) q
on p.id = q.id;
Let me know in case of any concerns
Thank you everyone for all the answers and different ways you posted to solve the query while i found a much simpler way to do same after having a discussion with one of my juniors. I felt pity for myself.
update e1 a set a.addr=(select addr from e1_addr b
where mod_date= (select max(mod_date) from e1_addr
where id= a.id ));
Thanks for all the support guys!

select distinct from join

My Tables look like this
Table 1 Table 2
Users Options
id name id user_id option
------- --- -------- --------
1 Donald 1 1 access1
2 John 2 1 access2
3 Bruce 3 1 access3
4 Paul 4 2 access1
5 Ronald 5 2 access3
6 Steve 6 3 access1
Now, i want to select join these to find a user which has only access1
If i do something like
select t1.id,t1.name,t2.id,t2.user_id,t2.option
from table1 t1, table2 t2
where t1.id=t2.user_id
and option='access1';
This does not give me unique results, as in the example i need only user_id=3 my data has has these in hundreds
I also tried something like
select user_id from table2 where option='access1'
and user_id not in (select user_id from table2 where option<>'access1')
There have been other unsuccessful attempts too but i am stuck here
You can do this using a EXISTS subquery (technically, a left semijoin):
SELECT id, name
FROM table1
WHERE EXISTS(
SELECT * FROM table2
WHERE table1.id = table2.user_id
AND table2.option = 'access1'
)
If you want only users that have access1 and not any other access, add NOT EXISTS (a left anti-semi-join; there's a term to impress your colleagues!):
AND NOT EXISTS (
SELECT * FROM table2
WHERE table1.id = table2.user_id
AND table2.option <> 'access1'
)
bool_and makes it simple
with users (id,name) as ( values
(1,'donald'),
(2,'john'),
(3,'bruce'),
(4,'paul'),
(5,'ronald'),
(6,'steve')
), options (id,user_id,option) as ( values
(1,1,'access1'),
(2,1,'access2'),
(3,1,'access3'),
(4,2,'access1'),
(5,2,'access3'),
(6,3,'access1')
)
select u.id, u.name
from
users u
inner join
options o on o.user_id = u.id
group by 1, 2
having bool_and(o.option = 'access1')
;
id | name
----+-------
3 | bruce
If you want the user that has only access1, I would use aggregation:
select user_id
from table2
group by user_id
having min(option) = max(option) and min(option) = 'access1';
WITH users(id,name) AS ( VALUES
(1,'Donald'),
(2,'John'),
(3,'Bruce'),
(4,'Paul'),
(5,'Ronald'),
(6,'Steve')
), options(id,user_id,option) AS ( VALUES
(1,1,'access1'),
(2,1,'access2'),
(3,1,'access3'),
(4,2,'access1'),
(5,2,'access3'),
(6,3,'access1')
), user_access_count AS (
SELECT op.user_id,count(op.option) AS access_count
FROM options op
WHERE EXISTS(
SELECT 1 FROM options
WHERE option = 'access1'
)
GROUP BY op.user_id
)
SELECT u.id,u.name
FROM users u
INNER JOIN user_access_count uac ON uac.user_id = u.id
WHERE uac.access_count = 1;

SQL query to check if user assigned with a specific value and does not a different value assigned to the same user?

We can have users with multiple valueIDs. In SQL Server I am trying to pull all users that have valueID of 3 but do not have valueID of 1 and 2. So Table A would have a user column and a valueID column.
How do I write a SQL query to do this?
Also, any good resources to get my SQL query skills up to par?
Much appreciated.
Try:
SELECT user
FROM A outer
WHERE valueID = 3
AND NOT EXISTS
(
SELECT 1
FROM A inner
WHERE
(
valueID = 1
OR valueID = 2
)
AND inner.user = outer.user
)
Here's one more.... (all users with valueid = 3 and nothing else)
SELECT [USER]
FROM A
GROUP BY [USER]
HAVING MAX(CASE WHEN valueid = 3 THEN 0 when valueid IN (1,2) THEN 1 END) = 0;
This query will return all users that have values (3 and 1) or (3 and 2) or just 3
select user
from mytable t1
where valueId = 3
and (select count(distinct valueId)
from mytable t2 where t2.user = t1.user
and valueId IN (1,2)) < 2
A simple way to get what you asked if you meant users with a value of 3 except those which also have values of 1 and 2
Select s.UserID From SomeTable s
Where s.UserID NOT In
(select s1s.UserID From SomeTable s1s
inner join SomeTable s2s On s1s.UserID = s2s.UserID
Where s1s.ValueId = 1 And s2s.ValueId = 2)
Or
Select s.UserID From SomeTable s
Left Join
(select s1s.UserID From SomeTable s1s
inner join SomeTable s2s On s1s.UserID = s2s.UserID
Where s1s.ValueId = 1 And s2s.ValueId = 2) both
Where both.userID is null
I'm not really sure what your table structure is, but it's likely the below query should work depending on column names...
SELECT user, valueID
FROM users
WHERE valueID = 3

Selecting max value from 2nd table in first table results

I have 2 tables as below-
Table I
ID DATE
1 05/11/12
2 23/11/12
3 29/11/12
4 04/10/12
5 20/11/12
And another table (IH) with the following info-
ID RECNO NOTE
1 1 Open
1 2 Update
1 3 Close
2 1 Open
2 2 Update
2 3 Hold
2 4 Close
3 1 Open
4 1 Open
4 2 Update
5 1 Open
I would like to output a result as shown below, displaying the Note field using the highest value of RecNo for each ID. So using the data above the output should be-
ID DATE NOTE
2 23/11/12 Close
3 29/11/12 Open
The code I have is-
SELECT I.ID, I.DATE, IH.NOTE FROM
I I, IH IH
JOIN (SELECT MAX([RECNO]) [RECNO] FROM
IH
GROUP BY RECNO) IH2 ON IH2.ID = IH.ID AND
IH2.[RECNO] = IH.[RECNO]
JOIN I I2 ON I2.ID = IH.ID WHERE
(I2.DATE>={TS ‘2012-11-22 00:00:002}) GROUP BY I2.ID
However when I execute the code I get-
Invalid Column Name 'RECNO'. Statement(s) could not be prepared.
How about this? Note, haven't tried it, I'm on my Mac at the moment.
SELECT I.ID, I.DATE, IH.NOTE
FROM I I
OUTER APPLY
(SELECT TOP 1 *
FROM IH
WHERE IH.ID = I.ID
ORDER BY RECNO DESC) IH
WHERE I.DATE >= '2012-11-22'
Your SQL is rather, uh, messy.
Assuming you are using SQL Server 2005 or greater, you can use the row_number() function, as follows:
SELECT I.ID, I.DATE, IH.NOTE
FROM I join
(select ih.*, ROW_NUMBER() over (PARTITION by id order by recno desc) as seqnum
from IH
) ih
on IH2.[RECNO] = IH.[RECNO] and seqnum = 1
WHERE I2.DATE>='2012-11-22 00:00:002'
This is assigning a sequence number in the IH table, for each id with the highest record number getting the value "1". The rest is just SQL.
Your original query is simply not correct syntactically, but I think this is what you want based on the description.
and another one
SELECT I.ID, I.DATE
,(Select TOP 1 IH.NOTE FROM IH where IH.ID=i.ID Order by Recno DESC) as Note
from I
WHERE
I.DATE>'20121122'
maybe this will help
SELECT a.ID, a.DATE, b.NOTE FROM a
inner join b on a.ID = b.ID
where b.recno in (select max(bb.recno)
from b as bb where bb.id = b.id)
http://sqlfiddle.com/#!3/fd141/2
If you don't mind the different identifiers, look at this solution:
select t1.MyID, t1.MyDate, y.Note
from t1
join
(
select MyID, max(RecNo) as RecNo
from t2
group by MyID
) x
on t1.MyID = x.MyID
left join
(
select *
from t2
) y
on t1.MyID = y.MyID
and x.RecNo = y.RecNo
where t1.MyDate >= '2012.11.22'
The complete solution is here: http://sqlfiddle.com/#!3/4ca09/3
Update: Oops, forgot to bring in the date in where clause. Updated SQL Fiddle and the query above.

SQL JOIN Statement

Lets say I have a table e.g
Request No. Type Status
---------------------------
1 New Renewed
and then another table
Action ID Request No LastUpdated
------------------------------------
1 1 06-10-2010
2 1 07-14-2010
3 1 09-30-2010
How can I join the second table with the first table but only get the latest record from the second table(e.g Last Updated DESC)
SELECT T1.RequestNo ,
T1.Type ,
T1.Status,
T2.ActionId ,
T2.LastUpdated
FROM TABLE1 T1
JOIN TABLE2 T2
ON T1.RequestNo = T2.RequestNo
WHERE NOT EXISTS
(SELECT *
FROM TABLE2 T2B
WHERE T2B.RequestNo = T2.RequestNo
AND T2B.LastUpdated > T2.LastUpdated
)
Using aggregates:
SELECT r.*, re.*
FROM REQUESTS r
JOIN REQUEST_EVENTS re ON re.request_no = r.request_no
JOIN (SELECT t.request_no,
MAX(t.lastupdated) AS latest
FROM REQUEST_EVENTS t
GROUP BY t.request_no) x ON x.request_no = re.request_no
AND x.latest = re.lastupdated
Using LEFT JOIN & NOT EXISTS:
SELECT r.*, re.*
FROM REQUESTS r
JOIN REQUEST_EVENTS re ON re.request_no = r.request_no
WHERE NOT EXISTS(SELECT NULL
FROM REQUEST_EVENTS re2
WHERE re2.request_no = r2.request_no
AND re2.LastUpdated > re.LastUpdated)
SELECT *
FROM REQUEST, ACTION
WHERE REQUEST.REQUESTNO = ACTION.REQUESTNO --Joining here
AND ACTION.LastUpdated = (SELECT MAX(LastUpdated) FROM ACTION WHERE REQUEST.REQUESTNO = ACTION.REQUESTNO);
A sub-query is used to get the last updated record's date and matches against itself to prevent the other records being joined.
Granted, depending on how precise the LastUpdated field is, it can have problems with two records being updated on the same date, but that is a problem encountered in any other implementation, so the precision would have to be increased or some other logic would have to be in place or another distinguishing characteristic to prevent multiple rows being returned.
SELECT r.RequestNo, r.Type, r.Status, a.ActionID, MAX(a.LastUpdated)
FROM Request r
INNER JOIN Action a ON r.RequestNo = a.RequestNo
GROUP BY r.RequestNo, r.Type, r.Status, a.ActionID
We can use the operation Top 1 with ORDER BY clause. For instance, if your tables are RequestTable(ID,Type,Status) and ActionTable(ActionID,RequestID,LastUpdated), the query will be like this:
Select Top 1 rq.ID, rq.Status, at.ActionID
From RequestTable as rq
JOIN ActionTable as at ON rq.ID = at.RequestID
Order by at.LastUpdated DESC