SQL Table design (select * from table where ( field = search1, and field = search2) - sql

PatientDX
Name Disease
Aa HIV
Aba DM
Bb HT
Bb DM
Aa HT
I want to get patient names who have both HIV, DM or all diseases or something like that. I want to make analysis by disease checkboxes in UI. How can I do it? Is my table design bad? Could you suggest me a better way to achieve that? Disease names can be as many as 100. So, I want to make it easy to find out patients who have particular 3 diseases, 4 or 5 and so on. Thank you.

Your schema is good.
Here is an example of how to query for patients having all 3 specific diseases:
select Name
from PatientDX
where Disease in ('HIV', 'DM', 'HT')
group by Name
having count(distinct Disease) = 3
A few important things to note here:
We use distinct in the having clause to make sure that if only 'HIV' was passed in 3 times (I'm assuming you will be using parameterized queries) we wouldn't get a result back.
The value you are comparing count to (in this case, 3) must match the number of values in the in clause.
The list of values in the IN clause must be unique before you count them.

Try grouping by user and then using having clause in your query. So to find a patient who has more than one disease you could query like:
SELECT NAME
FROM PatientDX
GROUP BY NAME
HAVING COUNT(DISTINCT Disease) > 1
If you wish to know patient who has all the diseases then you could do something like:
SELECT NAME
FROM PatientDX
GROUP BY NAME
HAVING COUNT(DISTINCT Disease) = (SELECT COUNT(DISTINCT Disease)
FROM PatientDX)-- although you could maintain disease in another table.

Related

Query to select person on a table that has a class name on another table

I've been at this for a while.
Basically it's a query that needs to identify:
"In which courses do people who only drink one type of beer teach?
(Name of course (s))"
Table 1 : The relation K contains information about courses: their course code (KKOD), course name (NAME), number of points (POINTS) and course abbreviation (NICKNAME).
Table 2: The relationship PB contains information on people's total beer consumption divided into different varieties: the person's name (NAME), Systembolaget's article number (SYSNR) and the number of liters consumed (LITER).
There's more tables but I believe only those two are needed to respond to the question.
First I wanted to write a query that identifies the people who only have drunken one type of beer.
select * from pb group by namn having count(*)=1;
Then I tried to write a query combining the above query to list the courses but here's where nothing happens or if I do some variations, all of the courses will list, not just the ones I need.
Try 1: select namn from pb group by namn having count(*)=1 where exists(select knamn from K where pb.namn = k.knamn);
Try 2: select knamn from k where exists(select namn from pb by group by namn having count(*)=1);
Doesn't work, any recommendations?
Thank you
The description of the tables seem to be lacking an ability to join them. Let's just assume you have a person_id or name in both tables.
This is the question:
"In which courses do people who only drink one type of beer teach? (Name of course (s))"
You can use a CTE to first obtain the names of people who drink only one type of beer, and use in a query such as this:
with beer_types as
(
select person_name
from beers
group by person_name
having count(distinct beer_type) = 1
)
select c.person_name, c.course_code
from courses c
join beer_types bt
on c.person_name = bt.person_name
DB-Fiddle found here.

How to do a subquery on a select statement to show something like this

I joined bunch of tables to get diagnosis for some people. I added a filter that only catches certain people who has certain diagnosis. In my example below, I separated those who have fever.
Here, Patient with ID 1 has 4 diagnosis and Patient with ID 2 has 3 diagnosis. They both have fever along with other problems.
Now I want show the other problem these folks have along with the fever diagnosis like this below example.
Is there any way I can group by Patient ID and show all of their diagnosis on one row? Like having a subquery on a select statement. I am not good with SQL so an example code would be very helpful.
You can group_concat
SELECT NAME, DateOfBirth, ID, GROUP_CONCAT(Diagnosis)
FROM <your_table>
GROUP BY NAME, DateOfBirth, ID

How to include zero results when querying one single table?

I have a table called Apartments that has three columns: apartment_type, person, date. It includes the apartment type selected by a certain person and date. I need to count how many people picked each of the apartment types. Some apartment type have 0 population.
Here is my query:
SELECT apartment_type, COUNT(*) AS TOTAL
FROM Apartments
GROUP BY apartment_type
It works great, but it doesn't include apartment types with a value of 0. Please, help me to correct this query.
In case some appartment_type have 0 population - your table will not contain any record with that type - so you must add some join from another table, where all apartment types exists. Or use union to create all 0 populated entries.
Something like:
SELECT apartment_type, COUNT(*) AS TOTAL
FROM (SELECT * FROM Apartments UNION ALL SELECT apartment_type, 0 as person, 0 as date from SomeTableWithFullListOfTypes group by apartment_type) as tmp
GROUP BY apartment_type
I generally agree with Nosyara's answer, but I don't agree with his sample query with the union all. I'm not sure it works, and it's certainly too complicated.
As stated already, if you don't have a table with all the possible apartment types, create one. Then you can write your query using a simple left join:
select t.apartment_type, count(a.apartment_type) as total
from apartment_types t
left join apartments a
on a.apartment_type = t.apartment_type
group by t.apartment_type
Note how count(*) was replaced by count(a.apartment_type). That change is necessary to have an accurate count in the case where you don't have apartments for a certain apartment type.
SELECT apartment_type, COUNT(apartment.*) AS TOTAL
FROM apartment_type
left join apartment
on apartment_type.aparentment_type = apartements.apartment_type
GROUP BY apartment_type
Using a left join will give you everything from the left side of the join (so all your types) and anything from the right that matches.

Why is a group by clause required when rows are limited in where clause?

fiId is a primary key of Table1. Why does this query return as many rows as there are fiId in table1. The fiId is being limited to 1 row in the where clause. The query performs properly when a group by Table1.fiId is added, surely this should not be needed? Thanks.
SELECT
Table1.fiId,
SUM(CASE Table2.type IN (4,7) THEN Table2.valueToSum ELSE 0 END),
FROM
Table1 INNER JOIN Table3 ON Table1.fiId = Table3.parentId
INNER JOIN Table2 ON Table2.leId = Table3.fiId
WHERE
Table1.fiId = 76813 AND
Table2.insId = 431144
When using aggregate functions in your SELECT such as SUM and COUNT when selecting other columns as well, a GROUP BY including those additional columns is required. While I don't know the exact reason behind this, it definitely helps to put the results in context.
Consider the following query:
SELECT Name, Count(Product) as NumOrders
FROM CustomerOrders
GROUP BY Name
Here, we assume that we will get results like this:
Name NumOrders
------------------
Joe 15
Sally 5
Jim 23
Now, if SQL did not require the GROUP BY, then what would you expect the output to be? My best guess would be something like this:
Name NumOrders
------------------
Joe 43
Sally 43
Jim 43
In that case, while there may in fact be 43 order records in the table, including Name doesn't really provide any useful data. Instead, we just have a bunch of names out of context.
For more on this, see a similar question here: Why do I need to explicitly specify all columns in a SQL "GROUP BY" clause - why not "GROUP BY *"?

Compare 2 columns in 2 tables with DISTINCT value

I am now creating a reporting service with visual business intelligent.
i try to count how many users have been created under an org_id.
but the report consist of multiple org_id. and i have difficulties on counting how many has been created under that particular org_id.
TBL_USER
USER_ID
0001122
0001234
ABC9999
DEF4545
DEF7676
TBL_ORG
ORG_ID
000
ABC
DEF
EXPECTED OUTPUT
TBL_RESULT
USER_CREATED
000 - 2
ABC - 1
DEF - 2
in my understanding, i need nested SELECT, but so far i have come to nothing.
SELECT COUNT(TBL_USER.USER_ID) AS Expr1
FROM TBL_USER INNER JOIN TBL_ORG
WHERE TBL_USER.USER_ID LIKE 'TBL_ORG.ORG_ID%')
this is totally wrong. but i hope it might give us clue.
It looks like the USER_ID value is the concatenation of your ORG_ID and something to make it unique. I'm assuming this is from a COTS product and nothing a human would have built.
Your desire is to find out how many entries there are by department. In SQL, when you read the word by in a requirement, that implies grouping. The action you want to take is to get a count and the reserved word for that is COUNT. Unless you need something out of the TBL_ORG, I see no need to join to it
SELECT
LEFT(T.USER_ID, 3) AS USER_CREATED
, COUNT(1) AS GroupCount
FROM
TBL_USER AS T
GROUP BY
LEFT(T.USER_ID, 3)
Anything that isn't in an aggregate (COUNT, SUM, AVG, etc) must be in your GROUP BY.
SQLFiddle
I updated the fiddle to also show how you could link to TBL_ORG if you need an element from the row in that table.
-- Need to have the friendly name for an org
-- Now we need to do the join
SELECT
LEFT(T.USER_ID, 3) AS USER_CREATED
, O.SOMETHING_ELSE
, COUNT(1) AS GroupCount
FROM
TBL_USER AS T
-- inner join assumes there will always be a match
INNER JOIN
TBL_ORG AS O
-- Using a function on a column is a performance killer
ON O.ORG_ID = LEFT(T.USER_ID, 3)
GROUP BY
LEFT(T.USER_ID, 3)
, O.SOMETHING_ELSE;