SQL query exclude a certain value from same column same table? - sql

Say I have a student table called ACADEMICDETAIL that looks something like this: (ADW stands for Add/Drop Wait and can have these values 'A' =add, 'D' =drop, 'W' =wait)
PeopleID | ADW | Class | Term | Year
1234 A Math Spring 2017
1234 D Spanish Spring 2017
1234 A Biol Fall 2016
1234 A Engl Fall 2016
0001 D Engl Spring 2017
0001 D Math Spring 2017
0001 D Biol Spring 2017
5601 W Math Spring 2017
2300 D Biol Spring 2017
2300 A Engl Fall 2016
My (bad) query looks something like this:
SELECT distinct PeopleId
FROM ACADEMICDETAIL
WHERE
Term = 'Spring' and
Year = '2017' and
ADW = 'D'
I need to only pull students who have dropped all classes for specific year/term. If some were dropped but others are still added, I don't need those students in results.
So the result I need to get:
PeopleID
0001
2300
Reality with my bad query:
PeopleID
0001
2300
1234
Any tips? I don't want the 1234 student to show up because that student has both adds and drops.

I think this is what you are looking for.
SELECT DISTINCT PeopleID
FROM ACADEMICDETAIL
WHERE ADW = 'D'
AND Term = 'Spring'
AND Year = '2017'
AND Peopleid NOT IN (SELECT DISTINCT PeopleId
FROM ACADEMICDETAIL
WHERE Term = 'Spring'
AND Year = '2017'
AND ADW IN ('A' , 'W'))

Think group by and having:
SELECT year, term PeopleId
FROM ACADEMICDETAIL
GROUP BY year, term, PeopleId
HAVING MIN(ADW) = 'D' and MAX(ADW) = 'D';
This selects all term/yearcombinations. You can of course use aWHERE` clause to limit to only one or a few.

Hi I think the result you are getting are correct. Base on your query there is actually 3 distinct row meet your condition.

try the solution below
-- to display 2017 Drop only
select distinct PeopleID from ACADEMICDETAIL t1
where adw = 'D' and year = 2017
and not exists (select 1 from ACADEMICDETAIL t2 where t1.year = t2.year and t1.adw <> t2.ADW and t1.PeopleID = t2.PeopleID)
-- to display all Drop years
select distinct PeopleID,Year from ACADEMICDETAIL t1
where adw = 'D'
and not exists (select 1 from ACADEMICDETAIL t2 where t1.year = t2.year and t1.adw <> t2.ADW and t1.PeopleID = t2.PeopleID)

You need to check for each PeopleID if there is any other AWD happened to exist for that particular ID.
SELECT distinct PeopleId
FROM ACADEMICDETAIL AS A
WHERE
Term = 'Spring' and Year = '2017' and
((Select count (*) from ACADEMICDETAIL as A1 where AWD = 'D' and A1.PeopleID = A.PeopleID) -
(Select count (*) from ACADEMICDETAIL as A2 where AWD = 'A' and A2.PeopleID = A.PeopleID) ) >= 0
EDIT1 :
I create the table, inserted the data in and ran the query.
You can see the results HERE

If you distinct your data. You will get actually 3 distinct value. In short your script and result is corret

Related

SQL - Display Name ID from Consecutive Occurrences of values in a Table

I have a table created, as an example 'Table1', see below;
Name Year
John 2003
Lyla 1994
Faith 1996
John 2002
Carol 2000
Carol 1999
John 2001
Carol 2002
Lyla 1996
Lyla 1997
Carol 2001
John 2009
Based on the above table, I have summarised my findings.
Carol participated for 4 years in a row; 1999, 2000, 2001, 2002
John participated for 3 years in a row; 2001, 2002, 2003 – John also participated in 2009, but this does not count as part of the streak.
Lyla participated in 1994, 1996, 1997 but these were not three consecutive years.
Faith participated only 1 time.
What I am looking to do is write a SQL query where only the Name Id in the table are displayed where the users have participated for 3 consecutive years or more, so I should only be getting the names of only 'Carol' and 'John' based on the above.
I am not exactly sure how to write this and would hope that someone could guide me.
I have only come up with a short and basic start like the one below, but in all honesty I am not sure that is even the correct way to go about it.
Select Name From Table1
Where Year = ?
Order by Name asc
Group by Year
Assuming you have one row per person per year, you can use lag() and select distinct:
select distinct name
from (select t.*,
lag(year, 2) over (partition by name order by name) as prev2_year
from table1 t
) t
where prev2_year = year - 2;
This simply looks back two rows for each name and compares the year on that row to the year on the current row. If there are three years in a row, then that year is exactly year - 2.
You could also do this with joins, but the above probably performs better:
select distinct t1.name
from table1 t1 join
table1 t1_1
on t1.name = t1_1.name and
t1.year = t1_1.year + 1 join
table1 t1_2
on t1.name = t1_2.name and
t1.year = t1_2.year + 2;
select
n1.name,
SUM(CASE WHEN n2.year is null then 0 else 1 end)+1 YearsInRow
from Table1 n1
left join Table1 n2 on n2.name=n1.name and (n2.year=n1.year+1 )
GROUP by n1.name
HAVING SUM(CASE WHEN n2.year is null then 0 else 1 end)+1 >=3
output:
name YearsInRow
---------- -----------
Carol 4
John 3

Select based on max date from another table

I'm trying to do a simple Select query by getting the country based on the MAX Last update from the other table.
Order#
1
2
3
4
The other table contains the country and the last update:
Order# Cntry Last Update
1 12/21/2019 9:19 PM
1 US 1/10/2020 1:07 AM
2 JP 7/29/2020 12:15 PM
3 CA 4/12/1992 2:04 PM
3 GB 11/6/2001 9:26 AM
3 DK 2/1/2005 3:04 AM
4 CN 8/20/2013 12:04 AM
4 10/1/2015 4:04 PM
My desired result:
Order# Country
1 US
2 JP
3 DK
4
Not sure the right solution for this. So far i'm stuck with this:
SELECT Main.[Order#], tempTable.Cntry
FROM Main
LEFT JOIN (
SELECT [Order#], Cntry, Max([Last Update]) as LatestDate FROM Country
GROUP BY [Order#], Cntry
) as tempTable ON Main.[Order#] = tempTable.[Order#];
Thanks in advance!
If needs only number of order and country,maybe don't need two tables:
SELECT distinct order, country
FROM
(
SELECT order, LAST_VALUE (country) OVER (PARTITION by [order] order by last_update) country FROM Country
) X
In SQL Server, you can use a correlated subquery:
update main
set country = (select top (1) s.country
from secondtable s
where s.order# = main.order#
order by s.lastupdate desc
);
EDIT:
A select would look quite simimilar:
select m.*,
(select top (1) country
from secondtable s
where s.order# = main.order#
order by s.lastupdate desc
)
from main m
I don't have time to try it with sample data, but is that what you are looking for?
select order orde, cntry
from table
where last_update =
(select max(last_update) from table where order = orde)

SQL query for until 2005 but not after 2005?

Find all Id who had taught until 2005 but had not taught after 2005.
for eg.
year ID
2010 A
2009 C
2005 B
2002 D
2002 C
2001 B
2000 A
Then the result should give only B and D.
The table has columns ID and year and I want to print out ID.
SELECT ID
FROM university.teaches
WHERE year <= 2005 AND
year NOT IN (SELECT year FROM university.teaches WHERE year> 2005);
I am trying something like this but it gives result including A and C
You should check for ids and not years with the IN operator:
SELECT DISTINCT ID
FROM university.teaches
WHERE id NOT IN (SELECT id FROM university.teaches WHERE year > 2005);
The subquery of IN returns all the ids that have taught after 2005, so NOT IN will return all the rest ids.
See the demo.
Results:
| ID |
| --- |
| B |
| D |
Use GROUP BY and MAX():
select id
from university.teaches
group by id
having max(year) <= 2005;

Column from another as parameter SQL SERVER

I have 2 table like this:
Table_Work
Name Date
===============
Andy 1 Jan
Andy 2 Jan
Andy 3 Jan
Ana 1 Jan
Ana 2 Jan
Ana 3 Jan
Ana 4 Jan
and Table_Salary
Name Salary
=================
Andy 150
Ana 120
I want to use column Name and Salary from table_salary as a parameter. So, I can show a data like this:
Name Salary_Got
===================
Andy 300
Ana 360
Table above show Salary_Got that calculate by (Salary x Number of Work). But I show only where work from 2 Jan. So, Andy only work for 2 days and Ana for 3 days.
Note:
This is only illustrated problem. My problem more complex than this. The point is I cannot use JOIN because I need to calculate other row.
I hope anyone can help me to solve this problem. Thanks in advance.
Try something like that;
select ts.Name, (ts.Salary * ISNULL(tw.workcound,0)) from Table_Salary ts left join
(select Name, count(*) workcount from Table_Work where Date != '2017-01-02' group by Name)
tw ON ts.Name = tw.Name
Try this:
select TW.Name, TW.DaysWorked * TS.Salary from (
select [Name], count(*) [DaysWorked] from Table_Work TW
where [Date] <> '1 Jan' --if you store date as text...
-- where [Date] > '2018-01-01' --if you store Date as date
group by [Name]
) TW join Table_Salary TS on TW.Name = TS.Name
TRY THIS: You can use outer apply to find out the count of days for each employees then multiply it will salary of the respected employee
SELECT ts.Name, (ts.Salary*ISNULL(t.tot, 1)) Salary_Got
FROM Table_Salary ts
OUTER APPLY(SELECT COUNT(*) tot
FROM Table_Work tw
WHERE tw.name = ts.name AND [Date] >= '2018-01-02') t

Specific Ordering in SQL

I have a SQL Server 2008 database. In this database, I have a result set that looks like the following:
ID Name Department LastOrderDate
-- ---- ---------- -------------
1 Golf Balls Sports 01/01/2015
2 Compact Disc Electronics 02/01/2015
3 Tires Automotive 01/15/2015
4 T-Shirt Clothing 01/10/2015
5 DVD Electronics 01/07/2015
6 Tennis Balls Sports 01/09/2015
7 Sweatshirt Clothing 01/04/2015
...
For some reason, my users want to get the results ordered by department, then last order date. However, not by department name. Instead, the departments will be in a specific order. For example, they want to see the results ordered by Electronics, Automotive, Sports, then Clothing. To throw another kink in works, I cannot update the table schema.
Is there a way to do this with a SQL Query? If so, how? Currently, I'm stuck at
SELECT *
FROM
vOrders o
ORDER BY
o.LastOrderDate
Thank you!
You can use case expression ;
order by case when department = 'Electronics' then 1
when department = 'Automotive' then 2
when department = 'Sports' then 3
when department = 'Clothing' then 4
else 5 end
create a table for the departments that has the name (or better id) of the department and the display order. then join to that table and order by the display order column.
alternatively you can do a order by case:
ORDER BY CASE WHEN Department = 'Electronics' THEN 1
WHEN Department = 'Automotive' THEN 2
...
END
(that is not recommended for larger tables)
Here solution with CTE
with c (iOrder, dept)
as (
Select 1, 'Electronics'
Union
Select 2, 'Automotive'
Union
Select 3, 'Sports'
Union
Select 4, 'Clothing'
)
Select * from c
SELECT o.*
FROM
vOrders o join c
on c.dept = o.Department
ORDER BY
c.iOrder