Side-by-side comparison of data by year in SQL - 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;

Related

SELECT salesman who had sales in particular years POSTGRES

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

subtract data from two columns and result into one column.`

I have data like below
id year marks id year marks
1 2017 80 1 2018 100
2 2017 60 2 2018 70
3 2017 500 3 2018 600
My result should be values as 20, 10, 100 in Difference column.
All this data should be in a single row.
Unless I'm missing something, you just want to take a difference of the two marks columns:
SELECT
t1.id, t1.year, t1.marks AS marks_2017, t2.id, t2.year, t2.marks AS marks_2018,
t2.marks - t1.marks AS diff
FROM yourTable t1
INNER JOIN yourTable t2
ON t1.id = t2.id AND t1.year = 2017 AND t2.year = 2018;
Somehow, I think you want:
select id, sum(case when year = 2018 then marks else - marks end) as diff
from t
where year in (2017, 2018)
group by id;
SELECT id, year, marks, id, year, new_marks, new_marks - marks AS diff
FROM yourTable where year = '2018'
select
t1.*, t2.year, t2.marks, t2.marks - t1.marks as diff
from
test t1
inner join
test t2
on t1.id = t2.id and t1.year = 2017 and t2.year = 2018
Demo

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;

Using distinct and case for two separate columns

I am counting cases by year using the following code:
COUNT(CASE WHEN Year(FilingDate)=2008 THEN 1 ELSE NULL END) AS '2008'
and I want to only count these cases when another column is distinct. The other column is called 'FilingDate' What I imagine would look something like this:
COUNT(CASE distinct (DocketNumber) WHEN Year(FilingDate)=2008 THEN 1 ELSE NULL END) AS '2008',
The Sample Records:
DocketNumber FilingDate
123 2008
123 2008
123 2008
111 2009
112 2009
I would just like to recieve = 1
Any Ideas?
Thanks
Use option with SUBQUERY and GROUP BY clause
SELECT COUNT(CASE WHEN YearFilingDate = 2008 THEN 1 END) AS '2008'
FROM
(
SELECT Year(FilingDate) AS YearFilingDate, DocketNumber
FROM dbo.test55
GROUP BY Year(FilingDate), DocketNumber
) x
Demo on SQLFiddle
SELECT COUNT(CASE WHEN Year(FilingDate)=2008 THEN 1 ELSE NULL END) AS '2008'
GROUP BY DocketNumber
may be.

SQL Lookup T1.ColumnA vs T2.ColumnA, if all 5 values in T1.ColumnA are listed in T2.ColumnA, Replace T1.ColumnA with "ALL"

I am trying to perform a lookup of specific values. If all values for Table1 for Region are listed as part of the lookup table (Table2), then replace the Table1.Region field with value = 'ALL', basically trying to group all 5 regions and subsitute the value with "ALL" when all 5 regions are listed (according to the values in the lookup table) When all regions ARE NOT listed, keep the Region Code
Year Region Value
2012 A1 24
2012 B2 24
2012 C3 24
2012 D4 24
2012 E5 24
2012 A1 36
2012 B2 36
2012 C3 36
2012 D4 36
2012 E5 36
2013 A1 24
2013 B2 24
Lookup Table
Region Value
1 A1
2 B2
3 C3
4 D4
5 E5
Result Desired
Year Region Term
2012 ALL 24 <-- Note region change to all because there are 5 regions per above
2012 ALL 36 <-- Note region change to all because there are 5 regions per above
2013 A1 24 <-- Region did not change because there were only 2 regions in source
2013 B2 24 <-- Region did not change because there were only 2 regions in source
I know how to group data but the grouped data needs to be consolidated further and replaced with ALL
Thanks for any direction!
Carlos
There's a little trick to this:
(#var IS NULL OR Table1.Region = #var)
you need to translate 'ALL' to null, so when 'ALL' is selected, it cancels out the filter.
I'd imagine you're either using SSRS, or a drop down above this, but here's a link:
https://www.simple-talk.com/content/print.aspx?article=835#hr2
You did not specify what RDBMS you are using but you should be able to use the following in all database products:
select distinct t1.year,
case
when t2.year is null and t2.value is null
then t1.region
else 'ALL' End Region,
t1.value
from table1 t1
left join
(
select year, value
from table1
group by year, value
having count(distinct region) = (select count(distinct value)
from table2)
) t2
on t1.year = t2.year
and t1.value = t2.value
See SQL Fiddle with Demo
Use this query with APPLY AND EXCEPT operators
SELECT t1.Year, CASE WHEN o.Value IS NULL THEN 'ALL' ELSE t1.Region END AS Region,
t1.Value
FROM dbo.test7 t1 OUTER APPLY (
SELECT Value
FROM test8
EXCEPT
SELECT Region
FROM test7 t2
WHERE t1.Year = t2.Year
AND t1.Value = t2.Value
) o
GROUP BY t1.Year, CASE WHEN o.Value IS NULL THEN 'ALL' ELSE t1.Region END,
t1.Value
Demo on SQLFiddle