sql - return all rows only if there is no field equal to A? - sql

I'm a bit stuck. I don't have my current SQL handy, but I'll try to be clear. Say I have a table of student IDs along with a course number and their grade. I want to return students only with no grades equal to A. I don't want just rows that aren't equal to A, but I am trying to find all student IDs with no associated A grades, otherwise students with at least one A will not be returned.
How might I tackle this problem? I'm fairly new to SQL. Thanks
EDIT: Okay, now that I have the actual SQL I can drop my terrible analogy.
SELECT ALL
ITEMS.QTY_ONHAND AS QTY_ONHAND,
ITEMS.ITEM_ID AS ITEM_ID_I0,
MEDORDER.MO_STAT AS MOMO_STAT,
UPPER(ITEMS.ITEM_ID) AS ITEM_ID_IC,
ITEMS.RX_DISP AS RX_DISP,
OMNIS.OMNI_ID AS OMNI_ID,
MEDORDER.ITEM_ID AS MOITEM_ID
FROM ITEMS ITEMS,
MEDORDER MEDORDER,
OMNIS OMNIS,
"PATIENTS" PATIENTS
WHERE (ITEMS.OMNI_STID = OMNIS.OMNI_STID) AND
(PATIENTS.PAT_ID = MEDORDER.PAT_ID) AND
(OMNIS.AREA = PATIENTS.AREA) AND (UPPER(ITEMS.ITEM_NAME) LIKE '%Doxycycline%'
AND MEDORDER.ITEM_ID = UPPER(ITEMS.ITEM_ID)
AND MEDORDER.MO_STAT='A')
ORDER BY ITEMS.QTY_ONHAND DESC, OMNIS.OMNI_ID, MEDORDER.ITEM_ID
What I have here is a location, omni_id. My query is looking for every omni_id that has this item_id. This will return multiple results for each omni_id corresponding to multiple medorders for the same item_id. What I want to do, is only return omni_ids that have 0 results for any medorder.mo_stat equal to 'A'. For example, omni_id 3W will have 3 returns, wth medorder_mo_stat equal to A, C, and C. And another, 4S, with medorder.mo_stats of C, C, C. I only want my query to return that 4S group, because it has no medorder.mo_stats of 'A'.
Sorry about the bad explanation.. I'm multitasking a few projects right now and my SQL-fu is not very strong, I'm not sure how to implement the below solutions to get what I'm looking for.
Thanks a ton in advance

Based you on your update - I assume this may help, but I'm still not sure I understand your schema, so maybe I am missing something...
SELECT
OMNIS.OMNI_ID AS OMNI_ID,
COUNT(MEDORDER.ITEM_ID) AS A_COUNT
FROM
ITEMS
JOIN
OMNIS
ON (ITEMS.OMNI_STID = OMNIS.OMNI_STID)
JOIN
PATIENTS
ON (OMNIS.AREA = PATIENTS.AREA)
LEFT JOIN
MEDORDER
ON (MEDORDER.ITEM_ID = UPPER(ITEMS.ITEM_ID)
AND (PATIENTS.PAT_ID = MEDORDER.PAT_ID)
AND (MEORDER.MO_STAT = 'A')
WHERE
UPPER(ITEMS.ITEM_NAME) LIKE '%Doxycycline%'
GROUP BY
OMNIS.OMNI_ID
HAVING
COUNT(MEDORDER.ITEM_ID) = 0
^EDIT^
Lost of ways to solve this one. I would do this:
SELECT
StudentID,
MAX(CASE Grade WHEN 'A' THEN 1 ELSE 0 END) AS HasAnA
FROM
MyTable
GROUP BY
StudentID
HAVING
MAX(CASE Grade WHEN 'A' THEN 1 ELSE 0 END) = 0
Or even just this depending on your table design:
SELECT DISTINCT
StudentID
FROM
MyTable
WHERE
Grade != 'A'

Related

SQL query question. Extracting data met for one of two conditions but not both

I'm extracting student data who have completed a list of courses for degree requirements. One of the courses on the list is equivalent to another course, so if a student completes both equivalent courses, it can only be counted once towards a degree. I need to extract data on students who completed the list of courses, while filtering for just one of the equivalent courses.
Where am I going wrong?
I've tried different OR and AND NOT clauses but I can't seem to get the result that I need
use coll18_live
select ENR_STUDENT_ID, ENR_TERM, CRS_NAME, ENR_GRADE
from dbo.CA320_ENROLLMENT_VIEW_N03
WHERE ENR_CENSUS_REG_FLAG = 'Y'
and ENR_TERM in ('14/FA', '15/SP')
and not (CRS_NAME = 'BUSI-105' and CRS_NAME = 'ENGL-120')
and CRS_NAME in ('ACCT-120', 'ACCT-125', 'BUSI-100', 'BUSI-103', 'BUSI-105', 'ENGL-120')
I expect the output to show students who completed ACCT-120, ACCT-12, BUSI-100, BUSI-103, and BUSI-105 or ENGL-120 (but not both BUSI-105 or ENGL-120)
I think you want aggregating with a having clause. You cannot do this with a WHERE, because the information you want is (apparently) in different rows:
select ENR_STUDENT_ID
from dbo.CA320_ENROLLMENT_VIEW_N03
where ENR_CENSUS_REG_FLAG = 'Y' AND
ENR_TERM in ('14/FA', '15/SP')
group by ENR_STUDENT_ID
having sum(case when CRS_NAME in ('ACCT-120', 'ACCT-125', 'BUSI-100', 'BUSI-103') then 1 else 0 end) = 4 and
sum(case when CRS_NAME in ('BUSI-105', 'ENGL-120') then 1 else 0 end) > 0;

How to to get two columns of data unrelated to each other in one sql query statement?

I need to get a state level count on number of services. For the purposes of this I only have two services. The first column is the states, the second column is the first services and the third column is the second service. What I am struggling with is to have the second and third column show up on the results in one query. Here is my code:
SELECT Distinct allstates.Name, count (data.StateName) as CareCase_Management_Services, count(data.StateName) Caregiver_Support_Services
From
(select distinct Name from USstate) allstates
Left Join
Client2017 data
on
allstates.Name = data.StateName and
data.FiscalYear = 2017 and
data.SrvstartCareCaseMgmtCode NOT IN('999','', '998') and
data.SrvstartCaregiverSuppCode NOT IN('999','', '998')
GROUP BY allstates.Name
ORDER BY allstates.Name ASC
I understand that you are looking to compute, for each state, the count of services that match certain criteria. There are two types of services, stored in two different columns.
If so, your query could be simplified using conditional aggregation :
SELECT
allstates.Name,
SUM(CASE WHEN c.SrvstartCareCaseMgmtCode NOT IN ('999', '', '998') THEN 1 ELSE 0 END) CareCase_Management_Services,
SUM(CASE WHEN c.SrvstartCaregiverSuppCode NOT IN ('999', '', '998') THEN 1 ELSE 0 END) Caregiver_Support_Services
FROM
(SELECT DISTINCT Name FROM USstate) s
LEFT JOIN Client2017 c ON s.Name = c.StateName AND c.FiscalYear = 2017
GROUP BY allstates.Name
With this technique, each service is counted according to its own logic ; when conditions are met, the record is counted in (1 is added to the SUM()), else it is ignored (+ 0).
NB : do you really have duplicated state names in USstate ? if no, you can replace subquery (SELECT DISTINCT Name FROM USstate) s with just USstate

Count of how many times id occurs in table SQL regexp

Hi I have a redshift table of articles that has a field on it that can contain many accounts. So there is a one to many relationship between articles to accounts.
However I want to create a new view where it lists the partner id's in one column and in another column a count of how many times the partner id appears in the articles table.
I've attempted to do this using regex and created a new redshift view, but am getting weird results where it doesn't always build properly. So one day it will say a partner appears 15 times, then the next 17, then the next 15, when the partner id count hasn't actually changed.
Any help would be greatly appreciated.
SELECT partner_id,
COUNT(DISTINCT id)
FROM (SELECT id,
partner_ids,
SPLIT_PART(partner_ids,',',i) partner_id
FROM positron_articles a
LEFT JOIN util.seq_0_to_500 s
ON s.i < regexp_count (partner_ids,',') + 2
OR s.i = 1
WHERE i > 0
AND regexp_count (partner_ids,',') = 0
ORDER BY id)
GROUP BY 1;
Let's start with some of the more obvious things and see if we can start to glean other information.
Next GROUP BY 1 on your outer query needs to be GROUP BY partner_id.
Next you don't need an order by in your INNER query and the database engine will probably do a better job optimizing performance without it so remove ORDER BY id.
If you want your final results to be ordered then add an ORDER BY partner_id or similar clause after your group by of your OUTER query.
It looks like there are also problems with how you are splitting a partnerid from partnerids but I am not positive about that because I need to understand your view and the data it provides to know how that affects your record count for partnerid.
Next your LEFT JOIN statement on the util.seq_0_to_500 I am pretty sure you can drop off the s.i = 1 as the first condition will satisfy that as well because 2 is greater than 1. However your left join really acts more like an inner join because you then exclude any non matches from positron_articles that don't have a s.i > 0.
Oddly then your entire join and inner query gets kind of discarded because you only want articles that have no commas in their partnerids: regexp_count (partner_ids,',') = 0
I would suggest posting the code for your util.seq_0_to_500 and if you have a partner table let use know about that as well because you can probably get your answer a lot easier with that additional table depending on how regexp_count works. I suspect regex_count(partnerids,partnerid) exampleregex_count('12345,678',1234) will return greater than 0 at which point you have no choice but to split the delimited strings into another table before counting or building a new matching function.
If regex_count only matches exact between commas and you have a partner table your query could be as easy as this:
SELECT
p.partner_id
,COUNT(a.id) AS ArticlesAppearedIn
FROM
positron_articles a
LEFT JOIN PARTNERTABLE p
ON regexp_count(a.partnerids,p.partnerid) > 0
GROUP BY
p.partner_id
I will actually correct myself as I just thought of a way to join a partner table without regexp_count. So if you have a partner table this might work for you. If not you will need to split strings. It basically tests to see if the partnerid is the entire partnerids, at the beginning, in the middle, or at the end of partnerids. If one of those is met then the records is returned.
SELECT
p.partner_id
,COUNT(a.id) AS ArticlesAppearedIn
FROM
PARTNERTABLE p
INNER JOIN positron_articles a
ON
(
CASE
WHEN a.partnerids = CAST(p.partnerid AS VARCHAR(100)) THEN 1
WHEN a.partnerids LIKE p.partnerid + ',%' THEN 1
WHEN a.partnerids LIKE '%,' + p.partnerid + ',%' THEN 1
WHEN a.partnerids LIKE '%,' + p.partnerid THEN 1
ELSE 0
END
) = 1
GROUP BY
p.partner_id

How would I write this SQL query?

I have the following tables:
PERSON_T DISEASE_T DRUG_T
========= ========== ========
PERSON_ID DISEASE_ID DRUG_ID
GENDER PERSON_ID PERSON_ID
NAME DISEASE_START_DATE DRUG_START_DATE
DISEASE_END_DATE DRUG_END_DATE
I want to write a query that takes an input of a disease id and returns one row for each person in the database with a column for the gender, a column for whether or not they have ever had the disease, and a column for each drug which specifies if they took the drug before contracting the disease. I.E. true would mean drug_start_date < disease_start_date. False would mean drug_start_date>disease_start_date or the person never took that particular drug.
We currently pull all of the data from the database and use Java to create a 2D array with all of these values. We are investigating moving this logic into the database. Is it possible to create a query that will return the result set as I want it or would I have to create a stored procedure? We are using Postgres, but I assume an SQL answer for another database will easily translate to Postgres.
Based on the info provided:
SELECT p.name,
p.gender,
CASE WHEN d.disease_id IS NULL THEN 'N' ELSE 'Y' END AS had_disease,
dt.drug_id
FROM PERSON p
LEFT JOIN DISEASE d ON d.person_id = p.person_id
AND d.disease_id = ?
LEFT JOIN DRUG_T dt ON dt.person_id = p.person_id
AND dt.drug_start_date < d.disease_start_date
..but there's going to be a lot of rows that will look duplicate except for the drug_id column.
You're essentially looking to create a cross-tab query with the drugs. While there are plenty of OLAP tools out there that can do this sort of thing (among all sorts of other slicing and dicing of the data), doing something like this in traditional SQL is not easy (and, in general, impossible to do without some sort of procedural syntax in all but the simplest scenarios).
You essentially have two options when doing this with SQL (well, more accurately, you have one option, and another more complicated but flexible option that derives from it):
Use a series of CASE statements in your query to produce columns that are representative of each individual drug. This requires knowing the list of variable values (i.e. drugs) ahead of time
Use a procedural SQL language, such as T-SQL, to dynamically construct a query that uses case statements as described above, but along with obtaining that list of values from the data itself.
The two options essentially do the same thing, you're just trading simplicity and ease of maintenance for flexibility in the second option.
For example, using option 1:
select
p.NAME,
p.GENDER,
(case when d.DISEASE_ID is null then 0 else 1 end) as HAD_DISEASE,
(case when sum(case when dr.DRUG_ID = 1 then 1 else 0 end) > 0 then 1 else 0 end) as TOOK_DRUG_1,
(case when sum(case when dr.DRUG_ID = 2 then 1 else 0 end) > 0 then 1 else 0 end) as TOOK_DRUG_2,
(case when sum(case when dr.DRUG_ID = 3 then 1 else 0 end) > 0 then 1 else 0 end) as TOOK_DRUG_3
from PERSON_T p
left join DISEASE_T d on d.PERSON_ID = p.PERSON_ID and d.DISEASE_ID = #DiseaseId
left join DRUG_T dr on dr.PERSON_ID = p.PERSON_ID and dr.DRUG_START_DATE < d.DISEASE_START_DATE
group by p.PERSON_ID, p.NAME, p.GENDER, d.DISEASE_ID
As you can tell, this gets a little laborious as you get outside of just a few potential values.
The other option is to construct this query dynamically. I don't know PostgreSQL and what, if any, procedural capabilities it has, but the overall procedure would be this:
Gather list of potential DRUG_ID values along with names for the columns
Prepare three string values: the SQL prefix (everything before the first drug-related CASE statement, the SQL stuffix (everything after the last drug-related CASE statement), and the dynamic portion
Construct the dynamic portion by combining drug CASE statements based upon the previously retrieved list
Combine them into a single (hopefully valid) SQL statement and execute

outer query to list only if its rowcount equates to inner subquery

Need help on a query using sql server 2005
I am having two tables
code
chargecode
chargeid
orgid
entry
chargeid
itemNo
rate
I need to list all the chargeids in entry table if it contains multiple entries having different chargeids
which got listed in code table having the same charge code.
data :
code
100,1,100
100,2,100
100,3,100
101,11,100
101,12,100
entry
1,x1,1
1,x2,2
2,x3,2
11,x4,1
11,x5,1
using the above data , it query should list chargeids 1 and 2 and not 11.
I got the way to know how many rows in entry satisfies the criteria, but m failing to get the chargeids
select count (distinct chargeId)
from entry where chargeid in (select chargeid from code where chargecode = (SELECT A.chargecode
from code as A join code as B
ON A.chargecode = B.chargeCode and A.chargetype = B.chargetype and A.orgId = B.orgId AND A.CHARGEID = b.CHARGEid
group by A.chargecode,A.orgid
having count(A.chargecode) > 1)
)
First off: I apologise for my completely inaccurate original answer.
The solution to your problem is a self-join. Self-joins are used when you want to select more than one row from the same table. In our case we want to select two charge IDs that have the same charge code:
SELECT DISTINCT c1.chargeid, c2.chargeid FROM code c1
JOIN code c2 ON c1.chargeid != c2.chargeid AND c1.chargecode = c2.chargecode
JOIN entry e1 ON e1.chargeid = c1.chargeid
JOIN entry e2 ON e2.chargeid = c2.chargeid
WHERE c1.chargeid < c2.chargeid
Explanation of this:
First we pick any two charge IDs from 'code'. The DISTINCT avoids duplicates. We make sure they're two different IDs and that they map to the same chargecode.
Then we join on 'entry' (twice) to make sure they both appear in the entry table.
This approach gives (for your example) the pairs (1,2) and (2,1). So we also insist on an ordering; this cuts to result set down to just (1,2), as you described.