MS SQL Where one column is x or y and both returned - sql

I have a table as follows with dates in. The table has many more records but simplified for asking purposes:
Name | Date | Grade
Person 1 | 01-01-2001 | B
Person 1 | 31-01-2001 | A
Person 2 | 01-01-2001 | C
Person 3 | 31-01-2001 | A
I want to return both records for Person 1 but not either of the other two. AND returns nothing obviously and OR returns everything. I want to search on the date not the grade or the person.
So the result would be:
Name | Date | Grade
Person 1 | 01-01-2001 | B
Person 1 | 31-01-2001 | A

One simple way to handle this is to aggregate by person and then assert that the two dates of interest are both present:
SELECT t1.*
FROM yourTable t1
INNER JOIN
(
SELECT Name
FROM yourTable
WHERE Date IN ('2001-01-01', '2001-01-31')
GROUP BY Name
HAVING COUNT(DISTINCT Date) = 2
) t2
ON t1.Name = t2.Name

You can uses EXISTS to return a row if there exists another row with that name, having the other A/B grade.
select t1.*
from tablename t1
where t1.Date in ('2001-01-01', '2001-01-31')
and exists (select 1 from tablename t2
where t2.Name = t1.Name
and t2.Date in ('2001-01-01', '2001-01-31')
and t2.Date <> t1.Date)

Related

SQL - How to pick the best available value for each column for each ID from multiple tables?

I have two tables with the same variables referring to attributes of a person.
How can I combine data from two such tables picking the best available value for each column from each table for each field?
Requirements:
For each field, I would like to fill it with a value from either one of the tables, giving a preference to table 1.
Values can be NULL in either table
In the combined table, the value for column 1 could come from table 2 (in case table 1 is missing a value for that person) and the value for column 2 could from table 1 (because both tables had a value, but the value from table 1 is preferred).
In my real example, I have many columns, so an elegant solution with less code duplication would be preferred.
Some users may exist in only one of the tables.
Example:
Table 1:
user_id | age | income
1 | NULL| 58000
2 | 22 | 60000
4 | 19 | 35000
Table 2:
user_id | age | income
1 | 55 | 55000
2 | 19 | NULL
3 | 22 | 33200
Desired output:
user_id | age | income
1 | 55 | 58000
2 | 22 | 60000
3 | 22 | 33200
4 | 19 | 35000
I think that's a full join and priorization logic with colaesce():
select user_id,
coalesce(t1.age, t2.age) as age,
coalesce(t1.income, t2.income) as income
from table1 t1
full join table2 t2 using(user_id)
Use full outer join if user_id in each table is unique.
SELECT
COALESCE(t1.user_id, t2.user_id) AS user_id,
GREATEST(t1.age, t2.age) AS age,
GREATEST(t1.income, t2.income) AS income
FROM t1
FULL OUTER JOIN t2 ON t1.user_id = t2.user_id
try like below using coalesce()
select t1.user_id, coalesce(t1.age,t2.age),
t1.income>t2.income then t1.income else t2.income end as income
table1 t1 join table2 t2 on t1.usesr_id=t2.user_id
You can use below code:
With TableA(Id,age,income) as
( --Select Common Data
select table_1.id,
--Select MAX AGE
case
when table_1.age> table_2.age or table_2.age is null then table_1.age else table_2.age
end,
--Select MAX Income
case
when table_1.income>table_2.income or table_2.income is null then table_1.income else table_2.income
end
from table_1 inner join table_2 on table_2.id=table_1.id
union all
-- Select Specific Data of Table 2
select table_2.id,table_2.age,table_2.income
from table_2
where table_2.id not in (select table_1.id from table_1)
union all
-- Select Specific Data of Table 1
select table_1.id,table_1.age,table_1.income
from table_1
where table_1.id not in (select table_2.id from table_2)
)select * from TableA

How do I output a table that an id have different gender in their game

I am not sure how to write the title of this question so the title might sound really confusing so please look at here. let suppose we have these two tables.
The first table is id, name, gender. This is the id of their game like login where name and gender is their name and gender in real life.
Where in the second table, name, and gender refer to in-game gender and name refer to in-game name as well.
id | name | gender id | name | Gender
---+------+---- ----+-------------+--------
1 | A | F 1 | a | F
2 | B | M and 1 | b | F
3 | C | M 2 | c | F
4 | D | M 3 | d | M
3 | e | M
3 | f | F
4 | g | M
4 | h | M
We want to select the id, name, and gender(in the first table), and the number of characters that an id had but that doesn't have the same gender as real-life. This might sound really confusing so here is what should be the output is
id | name | gender| #Character
---+-------+-------+-----------
2 B M 1
3 C M 3
id1: The reason it doesn't output id 1 is that id = 1 is F and she created 2 characters in the game, but both of the characters are F so she didn't 'switch' her gender so we do not print the row.
id2: We select id 2 because id = 2 is M in real-life, but he in-game character is F so we select this row.
id3: He is M in real life and one of his characters is F so we select this row
id4: He is M and none of his character is F so we don't need to print it on the screen.
Hopefully, you get what I'm trying to do here.
select id, name, gender,
(select count(*) from table_2 as t2 where t2.id = t1.id group by id) as #Character
from table_1 as t1
order by login;
Above query will print every id, name and gender and number of characters an id had, but this is not what I wanted. What should I change my code so that it works as what I Intended to do?
Actually it is simple.
We want to select the id, name, and gender(in the first table), and ... characters that an id had but that doesn't have the same gender as real-life.
select
*
from
t1
where
exists (select 1 from t2 where t1.id = t2.id and t1.gender <> t2.gender)
We want to select the id, name, and gender(in the first table), and the number of characters ...
select
*,
(select count(*) from t2 where t1.id = t2.id) as "#Character"
from
t1
where
exists (select 1 from t2 where t1.id = t2.id and t1.gender <> t2.gender)
Now that I understand the problem:
select t1.*, count(*)
from table_1 t1 join
table_2 t2
on t2.login = t1.login
group by t1.id
having count(*) filter (where t2.gender <> t1.gender) > 0;
In other words, you can filter in the having clause.
I think id and login could be confused -- your sample query does not match the sample data. But you should get the idea.
Try this:
select id,
(select count(*) from table_2 as t2 where t2.id = t1.id group by id having count(*) >1) as #Character
from table_1 as t1
order by login;

Update column with specific values ( if clause)

I am trying to insert new values into specific column with "update table set specific column...."
I have two tables like this:
Table1
Name Idnumber Score
JOHN DB 10
JOHN IT NULL
KAL DB 9
HENRY KK 7
KAL DB 10
HENRY IP 9
ALI IG 10
ALI PA 9
Table2
NAME | MONEY |
-----------------|
JOHN | |
-----------------|
KAL | |
-----------------|
HENRY | |
-----------------|
ALI | |
-----------------
And I want that my table look like this:
Updated Table
NAME | MONEY |
-----------------|
JOHN | |
-----------------|
KAL | yes |
-----------------|
HENRY | half |
-----------------|
ALI | yes |
-----------------
The condition for writing "yes" into money column is that all scores under same name in table1 should be 9 or higher, the condition for writing "half" is that scores should be no lower than 6 ( what I mean is that scores might be 10,9,8,7 and 6)
So basically it means, that, for example, Henry cannot be selected and I cannot write "yes" next to his name in updated table, because he has score under the value of 9 in one line , but in the other he has the score of 7.(null values also should be emitted).
I'm using a sas program. Thank you!!!
You seem to be treated NULL as a value that is less than "9". In standard SQL (which works in both MySQL and SAS, the original tags) is:
update table2 t2
set money = 'YES'
where not exists (select 1
from table1 t1
where t1.name = t2.name and coalesce(t1.score, 0) < 9
);
If you want to guarantee that there is at least one row in table2, one method is aggregation:
update table2 t2
set money = 'YES'
where (select (case when sum(case when t1.score > 9 then 1 else 0 end) = count(*)
then count(*)
end)
from table1 t1
where t1.name = t2.name
) > 0
You could try the following syntax:
update table2
set money = 'YES'
where not exists (select 1 from table1 t1 where t1.name = table2.name and t1.score < 9)
However this would also update records that have no match in table1. In MySQL, one simple option to avoid that uses a join:
update table2 t2
inner join (select name, min(score) min_score from table1 group by name) t1
on t1.name = t.name and t1.min_score >= 9
set t2.money = 'YES'

SQL query which checks, for each row having a date range in t1, whether there exists any recorded date in t2 within that date range?

If I have two tables like so:
Table 1
Start | END | More columns ...
------------------------------------------
2019-10-20 | 2019-10-21 |...
Table 2
Log ID | DATE
--------------
1 | 2019-10-20
2 | 2019-10-22
I've tried to use CASE WHEN, Boolean, Exists but I suspect my logic is wrong somewhere.
I want to return a results table which includes all of the columns from Table 1, with an additional column containing a Boolean value of whether a date within the range for that row exists in the second table.
So the result set should then look like:
Start | End | MoreCols | Available
----------------------------------------------
2019-10-20 | 2019-10-21 | … | True
Use exists:
select t1.*,
(case when exists (select 1
from table2 t2
where t2.date between t1.start and t1.end
)
then 'True' else 'False'
end) as available_flag
from table1 t1
If your database supports boolean types, then you can write this as:
select t1.*,
(exists (select 1
from table2 t2
where t2.date between t1.start and t1.end
)
) as available_flag
from table1 t1

MS Access: Compare 2 tables with duplicates

I have two tables which look like this:
T1:
ID | Date | Hour
T2:
ID | Date | Hour
I basically need to join these tables when their IDs, dates, and hours match. However, I only want to return the results from table 1 that do not match up with the results in table 2.
I know this seems simple, but where I'm stuck is the fact that there are multiple rows in table 1 that match up with table 2 (there are multiple intervals for any given hour). I need to return all of these intervals so long as they do not fall within the same hour period in table 2.
Example data:
T1:
1 | 1/1/2011 | 1
1 | 1/1/2011 | 1
1 | 1/1/2011 | 1
1 | 1/1/2011 | 2
T2:
1 | 1/1/2011 | 1
1 | 1/1/2011 | 1
My expected result set for this would be the last 2 rows from T1. Can anyone point me on the right track?.
I think you just want not exists:
select t1.*
from t1
where not exists (select 1
from t2
where t2.id = t1.id and t2.date = t1.date and t2.hour = t1.hour
);
EDIT:
I misread the question. This is very hard to do in MS Access. But, you can come close. The following returns the distinct rows in table 1 that do not have equivalent numbers in table 2:
select t1.id, t1.date, t1.hour, (t1.cnt - t2.cnt)
from (select id, date, hour, count(*) as cnt
from t1
group by id, date, hour
) t1 left join
(select id, date, hour, count(*) as cnt
from t2
group by id, date, hour
) t2 left join
on t2.id = t1.id and t2.date = t1.date and t2.hour = t1.hour
where t2.cnt < t1.cnt;