verify flag for user as parameter - sql

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).

Related

Horizontal Rows as Vertical Columns in SQL Server

I have this requirement where I need to present horizontal rows as vertical columns in SQL Server native query.
For example, my select query returns 100 records for the select query using inner join.
select XX, XX etc FROM TAB1 con inner join TAB2 reg on con.id = reg.reference_id WHERE con.is_active=1 and
con.approval_status=3 order by XXX
[![enter image description here][1]][1]
Now I want my query returning 1 records for 10 records where dynamic columns name should be created based on the business_ids for example 670_h_current, 671_h_current etc for all those 10 records. The result should be returned as a single query with 10 dynamic columns each represents a row value for business id and hcurrent.
I tried to use the Pivot table, but it does not seem working.Is there a way to manipulate the SQLs to return the required number of records. I am doing it at the moment in java but fetching all the records, then transforming. However, pagnation is an issue and my interface is slower as it picks a lot of records. I wanted the select query fetches a reduced view of records.
Any idea? Any help much appreciated
Current Select output
ID DEP LEG MAS EXE DES P1 BUS_ID ATTE
==================================================================
COI_AP002 215 216 4071 758 clients. NULL 673 1
COI_AP002 215 216 4071 758 clients. NULL 672 0
Expected Output
ID DEP LEG MAS EXE DES P1 ATTE_673 ATTE_672
==========================================================================
COI_AP002 215 216 4071 758 clients. NULL 1 0
Here's something to get you started. Then, you need to do a little work, to learn the concept, and to get it to do exactly what you want it to do.
--Drop Table dbo.Lateral
CREATE TABLE dbo.Lateral
( Id VARCHAR(100),
Dep int,
Des VARCHAR(100),
Bus_ID int,
Atte int)
--Delete from Account
INSERT INTO dbo.Lateral
(Id, Dep, Des, Bus_ID, Atte)
VALUES
('COI_AP002','215','clients',673,1)
INSERT INTO dbo.Lateral
(Id, Dep, Des, Bus_ID, Atte)
VALUES
('COI_AP002','215','clients',672,0)
Select *
From Lateral
select
x.ID,
sum(case when t.bus_id = '673' then 1 end) atte_673,
sum(case when t.bus_id =' 672' then 0 end) atte_672
from lateral t
cross apply (values
('COI_AP002' , t.bus_ID)
) as x(ID, val)
group by x.ID

NOT EXISTS is not working for multiple records which has same value

I am try to create a SQL query for the user should have revoke entry in Database for the respective grant access for that study.
For ex, if the user is having total count of 20 GRANT records then the user should have total count of REVOKE records. If not we need to enter the missing records.
I have created the following query. The query is working fine for the user who is having single Grant record for study but if the user is having Multiple grant record for particular study its not working.
For ex. User A is having two GRANT records and 1 REVOKE records for study ABC. the below query is not showing any output since the REVOKE record is already listed for that study.
But we have only one REVOKE records for that study. So I want to display that MISSING GRANT record
SELECT Getdate() [RequestedDate],
Getdate() [ApprovedDate],
A.requestedfor,
'XXX' [ApprovedBy],
A.projectnumber,
'Revoke' [AccessRequestType]
FROM [dbo].[Table1] A
WHERE A.requestedfor = 1234
AND A.accessrequesttype = 'Grant'
AND NOT EXISTS (SELECT *
FROM [dbo].[Table1] B
WHERE B.requestedfor =1234
AND B.accessrequesttype = 'Revoke'
AND A.projectnumber = B.projectnumber
AND A.accessgroup = B. accessgroup
AND A.RequestedFor = B. RequestedFor
)
I think you should use LEFT JOIN to link rows 1 to 1 and filter in WHERE clause by null fot that rows, where not exists linked pair by access type 'revoke'.
EXISTS clause checks only existed data. For example you want to insert new rows without duplicates.
SELECT a.[RequestedDate],
a.[ApprovedDate],
A.requestedfor,
a.ApprovedBy,
A.projectnumber,
'Revoke' [AccessRequestType]
FROM #Table1 A
LEFT JOIN #Table1 B ON B.accessrequesttype = 'Revoke'
AND A.projectnumber = B.projectnumber
AND A.accessgroup = B. accessgroup
AND A.RequestedFor = B. RequestedFor
WHERE A.requestedfor = 1234
AND A.accessrequesttype = 'Grant'
---this shows only rows, which not have pair 'Grant'-'Revoke'
AND B.ID IS NULL
Exists clause
Example result set to show an idea of query:
ReqDate
RequestedBy
AccessType
projNumber
accessGroup
RequestedFor
AccessType
2019-07-31
XXXXXX
Grant
1
1
1
Revoke
2019-07-30
XXXXX1
Grant
1
1
1
NULL
2019-07-29
XXXXXX
Grant
2
2
2
Revoke
2019-07-30
XXXXX1
Grant
1
1
1
NULL
2019-07-01
XXXXX1
Grant
3
2
2
NULL
By numbering the rows we can pair up GRANTs with REVOKEs, and return unmatched/excess GRANTs as new Revoke entries:
select
Getdate() [RequestedDate],
Getdate() [ApprovedDate],
A.requestedfor,
'XXX' [ApprovedBy],
A.projectnumber,
'Revoke' [AccessRequestType]
from (
SELECT requestedfor, projectnumber, seq=row_number() over (order by RequestedFor)
FROM Table1
where RequestedFor=1234
and AccessRequestType='Grant'
except
SELECT requestedfor, projectnumber, seq=row_number() over (order by RequestedFor)
FROM Table1
where RequestedFor=1234
and AccessRequestType='Revoke'
) a
```
#maxim's proposal should also work.

SQL Server cross-table join

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

Troubleshooting SQL Query

I have a Patient activity table that records every activity of the patient right from the time the patient got admitted to the hospital till the patient got discharged. Here is the table command
Create table activity
( activityid int PRIMARY KEY NOT NULL,
calendarid int
admissionID int,
activitydescription varchar(100),
admitTime datetime,
dischargetime datetime,
foreign key (admissionID) references admission(admissionID)
)
The data looks like this:
activityID calendarid admissionID activitydescription admitTime dischargeTime
1 100 10 Patient Admitted 1/1/2013 10:15 -1
2 100 10 Activity 1 -1 -1
3 100 10 Activity 2 -1 -1
4 100 10 Patient Discharged -1 1/4/2013 13:15
For every calendarID defined, the set of admissionid repeats. For a given calendarid, the admissionsid(s) are unique. For my analysis, I want to write a query to display admissionid, calendarid, admitTime and dischargetime.
select admissionId, calendarid, admitTime=
(select distinct admitTime
from activity a1
where a1.admisionID=a.admissionID and a1.calendarID=a.calendarid),
dischargeTime=
(select distinct dischargeTime
from activity a1
where a1.admisionID=a.admissionID and a1.calendarID=a.calendarid)
from activity a
where calendarid=100
When I individually assign numbers, it works, otherwise it comes up with this message:
Subquery returned more than 1 value.
What am I doing wrong?
DISTINCT does not return 1 row, it returns all distinct rows given the columns you provided in the select clause. That's why you're getting more than one value back from the subquery.
What are you looking for out of the sub-query? If you use TOP 1 instead of DISTINCT, that should work, but it might not be what you're looking for.
Your error message tells a lot. Obviously, one (or both) of your projection subqueries in the (the SELECT DISTINCT queries) return more than one value. Thus, the columns admitTime, resp. dischargeTime cannot be compared to the result.
One possibility would be to limit your subqueries to 1 row. However, this error might also indicate a structural problem in your DB design.
Try:
select top 1 admitTime
from activity a1
where a1.admisionID=a.admissionID and a1.calendarID=a.calendarid
or
select admitTime
from activity a1
where a1.admisionID=a.admissionID and a1.calendarID=a.calendarid
limit 1
This should get you what you want, with a bit less of a performance hit than subqueries:
select a1.admissionId
,a1.calendarid
,a2.admitTime
,a3.dischargeTime
from activity a1
left join activity a2
on a1.calendarid = a2.calendarid
and a2.admitTime <> -1
left join activity a3
on a1.calendarid = a3.calendarid
and a3.dischargeTime <> -1
where a1.calendarid=100
Try this !
select admissionId, calendarid, admitTime=
(select top(1) admitTime
from activity a1
where a1.admisionID=a.admissionID and a1.calendarID=a.calendarid),
dischargeTime=
(select top(1) dischargeTime
from activity a1
where a1.admisionID=a.admissionID and a1.calendarID=a.calendarid)
from activity a
where calendarid=100

Using count distinct to find records with 2 or more different values in a field

I have a simple question: How can I use Count(Distinct) in SQL (Oracle to be exact) to return only the rows where there are two or more different values in a given field.
This is easier understood by example:
ACCOUNT SALESMAN
123 Abc
123 Abc
246 Abc
246 Def
246 Def
369 Hij
456 Abc
456 Def
In this example, the only Accounts with 2 different sales reps would be 246 and 456, and thus, I'd want the query's result to just show the the accounts shared by 2 or more salesmen:
ACCOUNT SALESMAN
246 Abc
246 Def
456 Abc
456 Def
Thanks.
use having :
select distinct account,salesman
from MyTable where account in
(
select account
from MyTable
group by account
having count(distinct salesman) >= 2
)
order by 1,2
Here is a demonstration.
As the other answer has indicated you need to use HAVING, but not in the manor indicated. You need to join back to the original table after using HAVING
SELECT DISTINCT T.Account, T.SalesMan
FROM T
INNER JOIN
( SELECT Account
FROM T
GROUP BY Account
HAVING COUNT(DISTINCT SalesMan) > 1
) Dupes
ON Dupes.Account = T.Account
SQL Fiddle
You can do this with a simple GROUP BY/HAVING query:
select account
from t
group by account
having count(distinct salesperson) > 1
This returns the accounts, so the result is different from what you specify. One way to get the sales people is to use listagg:
select account, listagg(salesperson, ',')
from t
group by account
having count(distinct salesperson) > 1
Otherwise, Gareth's answer returns the results the way you specified in the question.