reclassify fields in one column of a table depending on criteria - sql

I have the following table:
person drug
------ -----
1 Y
2 Y
2 other
3 X
4 X
5 X
5 other
6 other
7 Z
However, if there is a person where they have a drug x,y,z (it will only be one distinct choice) plus 'other' - then I want to remove the row that contains other
This would mean that someone with an 'X' and 'other' would remove the row conatining 'other', but anyone with only 'other' will stay as 'other'. i.e.
person drug
------ -----
1 Y
2 Y
3 X
4 X
5 X
6 other
7 Z
where person 6 only has other, so stays that way, but persons 2 and 5 have the 'other' rows removed because they have other drug choices (x,y or z).
Thanks very much for any help.

It is unclear whether you want this removed in the results of a query or in the data itself. To return results without this row from a query, which can be written like this:
select t.*
from t
where not (t.drug = 'other' and
exists (select 1 from t t2 where t2.person = t.person and t2.drug = 'x')
)
To handle any of 'x', 'y', or 'z', change the last statement to t2.drug in ('x', 'y', 'z').

Related

What is the SQL query for returning Ids that contain only one certain value and nothing else?

let's say I have the following entries in a table with two columns:
Id
Value
1
F
1
N
1
N
2
F
3
N
3
N
3
N
4
N
5
F
5
N
I only want to output the Ids that have a value of 'N' and at the same time not the value 'F' in the second column. In this example my output from the query would be the Ids: 3 & 4.
Thank you very much!
If the database supports EXISITS, you can check for the existence of a row with the id and the value F.
SELECT
DISTINCT Id
FROM mytable m1
WHERE Value = 'N'
AND NOT EXISTS(SELECT 1 FROM mytable WHERE Value = 'F' AND m1.Id = Id)

How to check the value of any row in a group after a previous one fulfils a condition?

I have a dataset grouped by test subjects that is filled according to the actions they perform. I need to find which customer does A and then, at some point, does B; but it doesn't necessarily have to be in the next action/row. And it can't be first does B and then A, it has to be specifically in that order. For example, I have this table:
Subject ActionID ActionOrder
1 A 1
1 C 2
1 D 3
1 B 4
1 C 5
2 D 1
2 A 2
2 C 3
2 B 4
3 B 1
3 D 2
3 A 3
4 A 1
Here subjects 1 and 2 are the ones that fulfil the order of actions condition. While 3 does not because it performs the actions in reverse order. And 4 only does action A
How can I get only subjects 1 and 2 as results? Thank you very much
Use conditional aggregation:
SELECT Subject
FROM tablename
WHERE ActionID IN ('A', 'B')
GROUP BY Subject
HAVING MAX(CASE WHEN ActionID = 'A' THEN ActionOrder END) <
MIN(CASE WHEN ActionID = 'B' THEN ActionOrder END)
See the demo.
Consider below option
select Subject
from (
select Subject,
regexp_replace(string_agg(ActionID, '' order by ActionOrder), r'[^AB]', '') check
from `project.dataset.table`
group by Subject
)
where not starts_with(check, 'B')
and check like '%AB%'
Above assumes that Subject can potentially do same actions multiple times that's why few extra checks in where clause. Other wise it would be just check = 'AB'

how to get the even and odd column separately with separate column by query

I have an input:
id
1
2
3
4
5
6
7
8
9
10
I want get even and odd columns separately by columns in specified output like this
id col
1 2
3 4
5 6
7 8
9 10
here id and col are separate columns id contains the odd number and col contains the even number for specified input
SELECT MIN(id) as id, MAX(id) as col
FROM YourTable
GROUP BY FLOOR((id+1)/2)
For IDs 1 and 2, (id+1)/2 are 2/2 = 1 and 3/2 = 1.5, respectively, and FLOOR then returns 1 for both of them. Similarly, for 3 and 4, this is 2, and so on. So it groups all the input rows into pairs based on this formula. Then it uses MIN and MAX within each group to get the lower and higher IDs of the pairs.
Joined the table on itself
select *
from yourTable tA
left join yourTable tb on tA.id = (tB.id - 1)
where tA.id % 2 <> 0
If you use SQL you can try:
SELECT CASE WHEN column % 2 = 1
THEN column
ELSE null
END AS odds,
CASE WHEN column % 2 = 2
THEN column
ELSE null
END AS even
FROM yourtable
but not exactl as you ask
To show odd:
Select * from MEN where (RowID % 2) = 1
To show even:
Select * from MEN where (RowID % 2) = 0
Now, just join those two result sets and that's it.
Source

PLSQL or SSRS, How to select having all values in a group?

I have a table like this.
ID NAME VALUE
______________
1 A X
2 A Y
3 A Z
4 B X
5 B Y
6 C X
7 C Z
8 D Z
9 E X
And the query:
SELECT * FROM TABLE1 T WHERE T.VALUE IN (X,Z)
This query gives me
ID NAME VALUE
______________
1 A X
3 A Z
4 B X
6 C X
7 C Z
8 D Z
9 E X
But i want to see all values of names which have all params. So, only A and C have both X and Z values, and my desired result is:
ID NAME VALUE
______________
1 A X
2 A Y
3 A Z
6 C X
7 C Z
How can I get the desired result? No matter with sql or with reporting service. Maybe "GROUP BY ..... HAVING" clause will help, but I'm not sure.
By the way I dont know how many params will be in the list.
I realy appreciate any help.
The standard approach would be something like
SELECT id, name, value
FROM table1 a
WHERE name IN (SELECT name
FROM table1 b
WHERE b.value in (x,y)
GROUP BY name
HAVING COUNT(distinct value) = 2)
That would require that you determine how many values are in the list so that you can use a 2 in the HAVING clause if there are 2 elements, a 5 if there are 5 elements, etc. You could also use analytic functions
SELECT id, name, value
FROM (SELECT id,
name,
value,
count(distinct value) over (partition by name) cnt
FROM table1 t1
WHERE t1.value in (x,y))
WHERE cnt = 2
I prefer to structure these "sets within sets" of queries as an aggregatino. I find this is the most flexible approach:
select t.*
from t
where t.name in (select name
from t
group by name
having sum(case when value = 'X' then 1 else 0 end) > 0 and
sum9case when value = 'Y' then 1 else 0 end) > 0
)
The subquery for the in finds all names that have at least one X value and one Y value. Using the same logic, it is easy to adjust for other conditions (X and Y and Z,; X and Y but not Z and so on). The outer query just returns all the rows instead of the names.

count(*) from 2 tables with same columns

I have a weird problem at hand and I am sure I am very bad at SQL.
The problem is as follows:
I have 2 tables table1 and table2. Both the tables have same set of columns namely ID number, X number, Y number. Here X and Y will have value 0 or 1.
Now say for example ID ranges from 1-100 in table1 and ranges from 91-200 in table2: in table1, X has values for all the 100 rows say 1, and in the same table Y has values for only 90 say 1. The next 10 values of Y that is 91-100 are in table2.
Now, calling a count(*) for various queries say X=1 and Y=1, X=1 and Y=0 etc I do not get the correct values because some set of Y values are present in table2. I was looking at left Join but somehow I am unable to figure out if this is the correct approach.
Table1
-------
Id X Y
1 1 1
2 1 1
3 1 1
4 1 0
5 1 0
Table2
-------
Id X Y
4 0 1
5 0 1
6 0 0
7 0 0
8 0 0
9 1 1
So If I say X=1 and Y=1 I should get 5 as the count(*).
Hi Justin,
Let me explain you the actual scenario. Consider 3 processes p1,p2 and p3 which have a cache where P1, P2 and P3 are nothing but X,Y and Z columns. The content of this cache is nothing but the ID's. IF p1 dumps and Id 1 I would say X=1 for ID=1 etc. Each of these processes dumps cache in a group say g1 and g2. I have created a table with respect to g1 and g2. So g1 represent table1 and g2 represent table2. Each of p1, p2 and p3 have a limit of say dumping 100 IDs. It is possible that P1 has dumped ID's 1-100 in g1(table1) where p2 has dumped just 90 in g1(table1) and rest 10 in g2(table2), similarly p3 would have say dumped 95 in g1(table1) and rest 5 in g2(table2). However each of p1, p2 and p3 have dumped 100 ids but in different groups. Now if I want to get a count() in a ideal case when all the P1, P2 and P3 have dumped cache in g1 I would say get me max(id) from g1 where P1=1 and similarly min(id) from g1 where P1=1. Than I would write a query saying "Select count() from g1 where X=1 and Y=1 and Z=1 where ID between min(id) from g1 and max(id) from g1. In an ideal case it would have returned 100.But in current case it returns 90 which is not correct. So to resolve this issue I will also have to consider the ID's which are present in g2(table2) also.
I hope this answers your question.
Thanks
mav
It sounds like you want to UNION or UNION ALL the two tables together before applying the predicates. Something like
SELECT COUNT(*)
FROM (SELECT id, x, y
FROM table1
UNION ALL
SELECT id, x, y
FROM table2)
WHERE x = 1
AND y = 1;
UNION ALL will return every row from both tables. UNION will eliminate duplicate rows.
If this is not what you want, it would be very helpful to walk through an example where you create a few rows of sample data in each table and show us exactly the result you want and how you obtained that result.
SELECT COUNT(*)
FROM
(SELECT ID, MAX(X) X, MAX(Y) Y
FROM
(SELECT *
FROM TABLE1
UNION ALL
SELECT *
FROM TABLE2)
GROUP BY ID)
WHERE X = 1 AND Y = 1
Or if you wanna use an advanced group by clause
SELECT COUNT(*)
FROM
(SELECT *
FROM TABLE1
UNION ALL
SELECT *
FROM TABLE2)
HAVING MAX(X) = 1 AND MAX(Y) = 1
GROUP BY ID