SELECT salesman who had sales in particular years POSTGRES - sql

I want to find a salesman who had sales in 1998, 1999 and 2001
For example:
**year salesmen**
1998 a
1998 a
1998 b
1999 a
1999 b
1999 c
2001 a
2001 b
2001 c
result should be
"a" and "b" because only those salesman had sales in all years
I don't think it requires complex query but I could not come up with solution

Aggregation is one option:
SELECT salesmen
FROM yourTable
WHERE year IN (1998, 1999, 2001)
GROUP BY salesmen
HAVING COUNT(DISTINCT year) = 3;

This query shows salesmen that had sales in all 3 years(1998,1999,2001) at least once with only 1 read on the table:
select salesmen
from your_table
group by salesmen having count(case when year = 1998 then 1 end)>0 and
count(case when year = 1999 then 1 end)>0 and
count(case when year = 2001 then 1 end)>0
DB<>FIDDLE LINK

There are many options to get the desired result. One is using HAVING and COUNT or GROUP BY (there are already answers that show this way). I want to show the option to do not query harcoded the 3 years, but to do this instead:
SELECT salesmen
FROM yourTable
WHERE year IN (SELECT year FROM yourtable)
GROUP BY salesmen
HAVING COUNT(DISTINCT year) = (SELECT COUNT(DISTINCT year) FROM yourtable);
If this is intended, this will be the way to select those salesmen that occur in every year that appears in your table.
To come back to your question, another possibility is to use IN:
SELECT DISTINCT salesmen FROM yourtable
WHERE salesmen IN
(SELECT salesmen FROM yourtable WHERE year = 1998) AND salesmen IN
(SELECT salesmen FROM yourtable WHERE year = 1999) AND salesmen IN
(SELECT salesmen FROM yourtable WHERE year = 2001);
You can also use EXISTS:
SELECT DISTINCT salesmen FROM yourtable y
WHERE
EXISTS (SELECT 1 FROM yourtable WHERE year = 1998 AND salesmen = y.salesmen) AND
EXISTS (SELECT 1 FROM yourtable WHERE year = 1999 AND salesmen = y.salesmen) AND
EXISTS (SELECT 1 FROM yourtable WHERE year = 2001 AND salesmen = y.salesmen);
You can see the difference here and try out: db<>fiddle

Related

How to sort by values in a column based on identifiers in a different column in sql

This would be our input:
NAME
YEAR
VALUES
A
2001
10
A
2000
5
A
TOTAL
15
B
2010
3
B
2011
7
B
TOTAL
10
We have to sort this table by ascending values of 'TOTAL' and then by ascending values of YEAR within the same NAME.
This would be our output:
NAME
YEAR
VALUES
B
2010
3
B
2011
7
B
TOTAL
10
A
2000
5
A
2001
10
A
TOTAL
15
I think I'd sum all values partitioned by name in a CTE, sort by them, then name, then year (with a special case for total)
WITH cte AS (
SELECT *, SUM(values) OVER(PARTITION BY name) as sumval
FROM yourdata
)
SELECT name, year, values
FROM cte
ORDER BY sumval, name, CASE WHEN year = 'TOTAL' THEN '9999' ELSE year end
You could also join your data to itself on just the total lines, meaning every row of yourdata ends up with an associated totals row:
SELECT yourdata.*
FROM
yourdata
INNER JOIN (SELECT name, values FROM yourdata WHERE year = 'TOTAL') t
ON t.name = yourdata.name
ORDER BY
t.values, yourdata.name, CASE WHEN year = 'TOTAL' THEN '9999' ELSE year end
e.g.
B 2010 3 TOTAL 10
B 2011 7 TOTAL 10
B TOTAL 10 TOTAL 10
A 2000 5 TOTAL 15
A 2001 10 TOTAL 10
A TOTAL 15 TOTAL 10
The final column is the total row for the name repeated over and over, so you can sort by the total, then by name to break ties, then by year (with 9999 to put the total after the numeric years)

Select all Persons that NOT continuously present in SQL

I have the table Persons with the following contents:
year name
2015 John
2016 John
2017 John
2015 Mary
2015 Jennifer
2016 Jennifer
2015 Lisa
2016 Lisa
2017 Lisa
How can I get all Persons that not continuously present in all the years?
The Answer should be:
2015 Mary
2015 Jennifer
2016 Jennifer
One approach would be to aggregate by name and then assert that a given name's distinct count of years does not equal the total distinct number of years in the entire table.
SELECT t1.name
FROM yourTable t1
INNER JOIN
(
SELECT name
FROM yourTable
GROUP BY name
HAVING COUNT(DISTINCT year) < (SELECT COUNT(DISTINCT year) FROM yourTable)
) t2
ON t1.name = t2.name
ORDER BY
t1.name,
t1.year;
Demo
You can use below query,
select * from persons p1
where exists
(select name, count(1) from persons p2 where p1.name=p2.name group by name having count(1) <
(select count(distinct year) from persons));
You can use below query:
SQLfiddle
select a.name, a.year1
from name_test a
inner join
(select name, count(year1)
from name_test
group by name
having count(year1) < (select count(distinct year1) from name_test))
b on (a.name = b.name);
I would suggest window functions. Assuming no duplicates per name:
select p.*
from (select p.*,
count(*) over (partition by name) as cnt_name,
count(distinct year) over () as cnt_years
from persons p
) p
where cnt_name <> cnt_years;

Find duplicate values with different year in SQL

How can I search duplicate records in a table but has different years.
My sample data:
Cus_No Item_No Ord_Dt Orders
1 A 2016 1
1 A 2017 2
1 B 2016 1
2 B 2015 1
2 B 2018 1
Output needed
Cus_No Item_No Ord_Dt Orders
1 A 2016 1
1 A 2017 2
2 B 2015 1
2 B 2018 1
I am trying to collect all records with the same Cus_No, the same Item_No that has any value in Orders and exist in any year in Ord_dt. The reason is, I need to find those items with the same customer number that has orders from all years.
I am using MS Query and this is the SQL statement I tried but still displays all records.
SELECT `'table'`.Cus_No, `'table'`.Item_No, `'table'`.Ord_Dt, `'table'`.Orders
FROM `'table'`
WHERE (`'table'`.Orders>=1) AND (`'table'`.Ord_Dt In ('2016','2017'))
Assuming you want to identify records corresponding to customer items which appeared across more than one year, we can try the following:
SELECT t1.*
FROM yourTable t1
INNER JOIN
(
SELECT Cus_No, Item_No
FROM yourTable
GROUP BY Cus_No, Item_No
HAVING COUNT(DISTINCT Ord_Dt) > 1
) t2
ON t1.Cus_No = t2.Cus_No AND
t1.Item_No = t2.Item_No
Below query returns duplicates -
select * from test where (cus_no, item_no) in (
select Cus_No, item_no from test group by Cus_No, item_no having count(*) > 1)
Stab in the dark:
with agg as (
select cust_no, item_no from T
group by cust_no, item_no
where ord_dt in (...)
having count(*) = <num years in list>
)
select *
from T t inner join agg a
on a.cust_no = t.cust_no
and a.item_no = t.item_no;

SQL: Select id from table grouping by max year and name

I have the following data:
ID Year Name
1 2016 A
2 2015 A
3 2014 A
4 2014 B
5 2015 B
6 2010 C
7 2007 D
8 2008 D
9 2006 D
I need to query just the ID of the max date for each name group
Result: [1, 5, 6, 8 ]
which is really:
ID Year Name
1 2016 A
5 2015 B
6 2010 C
8 2008 D
I have the following, but don't know where to go from here
SELECT MAX(year) from table GROUP BY name
Ideally there should be no duplicate year and name groups, but if the there are duplicate records, then its possible. Since they would be duplicates, it would not matter which to keep.
If you want one row per name then I would recommend distinct on:
select distinct on (name) t.*
from t
order by name, year desc;
If you have duplicates, then one solution is rank():
select id, year, name
from (select t.*, rank() over (partition by name order by year desc) as seqnum
from t
) t
where seqnum = 1;
One could use row_number() analytic partitioned by name and ordered by year and ID desc to get the max ID for the max date. You've not indicated if ties exist what you'd like to see... but this would return one of them (the one with the highest ID.)
SELECT *
FROM (SELECT ID
, year
, Name
, row_number() over (PARTITION BY Name ORDER BY Year Desc, ID Desc) RN
FROM tbl) Z
WHERE RN = 1
An alternative way to accomplish this is to use your query as a inline view and simply join it back to the base set.
SELECT *
FROM tbl A
INNER JOIN (SELECT max(year) mYear, name
FROM tbl
GROUP BY name) B
on A.year = B.myear
and A.Name = B.Name
Ties will be displayed. So if you have a name with two records having a max year of 2016, then both records would be returned.

Side-by-side comparison of data by year in SQL

I have table similar to the following:
Year | Product | Value
2006 A 10
2006 B 20
2006 C 30
2007 A 40
2007 B 50
2007 C 60
I would like a query that would return the following comparison
Product | 2006 Value | 2007 Value
A 10 40
B 20 50
C 30 60
What are the options to do so? Can it be done without joins?
I'm working with DB2, but answers in all SQL types would be helpful.
select Product,
max(case when Year = 2006 then Value end) as [2006 Value],
max(case when Year = 2007 then Value end) as [2007 Value]
from MyTable
group by Product
order by Product
A simple cross tab query should do it
SELECT DISTINCT (year), PRODUCT,
sum (case when year = 2006 then VALUE else 0 end ) as [2006 Value]
sum (case when year = 2007 then value else 0 end ) as [2007 value]
from table
group by year, product
Check the syntax, but that's the basic idea. There is really no need to join.
You've already got some answers that don't use a join, but just for comparison here's an alternative that does use a join:
SELECT
T1.Product,
T1.Value AS [2006 Value],
T2.Value AS [2007 Value]
FROM Table1 T1
JOIN Table1 T2
ON T1.Product = T2.Product
AND T1.Year = 2006
AND T2.Year = 2007
I'm working with DB2, but answers in all SQL types would be helpful.
Here's a solution using PIVOT that SQL Server supports:
SELECT
Product,
[2006] AS [2006 Value],
[2007] AS [2007 Value]
FROM Table1
PIVOT(MAX(Value) FOR Year IN ([2006], [2007]))
AS p;