SQL - UNION ALL instead of JOIN - sql

Can someone please help me figure out how to write this script using UNION ALL? Currently it is using JOIN instead of UNION ALL.
SELECT
ISNULL(#vol2016.Destination, #vol2017.Destination) AS [Destination],
ISNULL(#vol2016.[Volume 2016],0) AS [Volume 2016],
ISNULL(#vol2017.[Volume 2017],0) AS [Volume 2017],
ISNULL(#vol2017.[Volume 2017],0) - ISNULL(#vol2016.[Volume 2016],0) AS [Developement]
FROM(
SELECT
[year], [Destination], CAST(SUM([Volume]) as INT) as [Volume 2016]
FROM [dbo].[dw_lc_full_aggregated_1]
WHERE [entry_type] = 'Country upload'
AND [year]='2016'
GROUP BY [year], [Destination]) #vol2016
FULL JOIN
(SELECT
[year], [Destination], CAST(SUM([Volume]) as INT) as [Volume 2017]
FROM [dbo].[dw_lc_full_aggregated_1]
WHERE [entry_type] = 'Country upload' AND [year]='2017'
GROUP BY [year], [Destination]) #vol2017
ON [#vol2016].Destination=[#vol2017].Destination
So the output should be in three columns. Destination, Volume 2016, Volume 2017.

Use this query:
SELECT [year]
, [Destination]
, CAST(SUM([Volume]) as INT) AS [Volume]
FROM
(
SELECT [year]
, [Destination]
, [Volume]
FROM [dbo].[dw_lc_full_aggregated_1]
WHERE [entry_type] = 'Country upload' AND [year]='2016'
UNION ALL
SELECT [year]
, [Destination]
, [Volume]
FROM [dbo].[dw_lc_full_aggregated_1]
WHERE [entry_type] = 'Country upload' AND [year]='2017'
) AS T
GROUP BY [year],[Destination]

You can actually make life a lot more simple for yourself with conditional aggregation (A CASE statement inside the SUM())
SELECT
[Destination],
[Volume_2016],
[Volume_2017],
[Volume_2017] - [Volume_2016] AS [Development]
FROM
(
SELECT
[Destination],
CAST(SUM(CASE WHEN [year] = '2016' THEN [Volume] END) as INT) [Volume_2016],
CAST(SUM(CASE WHEN [year] = '2017' THEN [Volume] END) as INT) [Volume_2017]
FROM
[dbo].[dw_lc_full_aggregated_1]
WHERE
[entry_type] = 'Country upload'
AND [year] IN ('2016', '2017')
GROUP BY
[Destination]
)
AS pivoted_agreggate
EDIT: [Year] removed from GROUP BY (copy and paste error)

Related

How to use non-aggregate columns used in the SELECT clause without using it in GROUP BY clause?

I am running this query and I get correct result
SELECT
CONCAT("year", ' - ', TRIM('20' FROM "year") + 1) as 'Years',
SUM("svalue") as 'Value',
'Sale' as 'Type',
"year" as 'Year',
"code" as 'FACode',
FROM "FCJOIN"
WHERE "code" IN
(
SELECT "fccode"
FROM "fcdetails"
)
AND "month" between '04' and '12'
AND "year" IN ( '2016' , '2017' , '2018' , '2019' )
GROUP BY "code", "year"
what i need is , if I add a new column formonth it will gives an error Improper usage of GROUP BY clause ? Please ensure that all non-aggregate columns used in the SELECT clause are also used in GROUP BY clause.
that's my modified query is like this
SELECT
CONCAT("year", ' - ', TRIM('20' FROM "year") + 1) as 'Years',
SUM("svalue") as 'Value',
'Sale' as 'Type',
"year" as 'Year',
"code" as 'FACode',
**"formonth" as 'Period'**
FROM "FCJOIN"
WHERE "code" IN
(
SELECT "fccode"
FROM "fcdetails"
)
AND "month" between '04' and '12'
AND "year" IN ( '2016' , '2017' , '2018' , '2019' )
GROUP BY "code", "year"
is there any way to make this correct group by or or there any way to rewrite ? I dont want the newly added column in group by clause. i am trying this in zoho reports. Any help ?
if you want to show the months of each year for which the code was applicable, then you could aggregate their descriptions/strings with string_agg(). For multiple levels of aggregation i.e sum(svalue) group by (code, year) or group by (year, month description/period) GROUPING SETS could be used.
declare #fcdetails table (fccode int)
insert into #fcdetails(fccode) values(1), (2), (3);
declare #fcjoin table
(
[year] char(4),
[month] tinyint,
svalue int,
code int
) ;
insert into #fcjoin([year], [month], svalue, code)
values
('2016', 5, 10, 1), ('2016', 6, 10, 1), ('2016', 7, 10, 1),
('2017', 5, 4, 2), ('2017', 6, 4, 2),
('2018', 7, 10, 3);
declare #othertable table
(
[themonth] tinyint,
period varchar(20)
);
insert into #othertable([themonth], period)
select distinct [month], {fn MONTHNAME(datefromparts('2020', [month], 1))}
from #fcjoin;
SELECT
CONCAT("year", ' - ', TRIM('20' FROM "year") + 1),
SUM("svalue") as 'Value',
'Sale' as 'Type',
"year" as 'Year',
"code" as 'FACode',
string_agg(o.period, ',') as forperiod
FROM #FCJOIN as f
join #othertable as o on f.month = o.themonth --careful with joins & aggregations
WHERE "code" IN
(
SELECT "fccode"
FROM #fcdetails
)
AND "month" between '04' and '12'
AND "year" IN ( '2016' , '2017' , '2018' , '2019' )
GROUP BY "code", "year";
SELECT
CONCAT("year", ' - ', TRIM('20' FROM "year") + 1),
SUM("svalue") as 'Value',
'Sale' as 'Type',
"year" as 'Year',
"code" as 'FACode',
(select string_agg(o.period, ',')
from #othertable as o
where o.themonth in
(select b.month from #FCJOIN as b where b.year = f.year and b.code = f.code)
) as forperiod
FROM #FCJOIN as f
WHERE "code" IN
(
SELECT "fccode"
FROM #fcdetails
)
AND "month" between '04' and '12'
AND "year" IN ( '2016' , '2017' , '2018' , '2019' )
GROUP BY "code", "year";
SELECT
CONCAT("year", ' - ', TRIM('20' FROM "year") + 1),
SUM("svalue") as 'Value',
'Sale' as 'Type',
"year" as 'Year',
"code" as 'FACode',
o.period as forperiod
FROM #FCJOIN as f
join #othertable as o on f.month = o.themonth --careful with joins & aggregations
WHERE "code" IN
(
SELECT "fccode"
FROM #fcdetails
)
AND "month" between '04' and '12'
AND "year" IN ( '2016' , '2017' , '2018' , '2019' )
GROUP BY GROUPING SETS(("code", "year"),("year", o.period) /*,(code, o.period)*/);

How do I put condition in MAX in GROUP BY

I want to group my data by Date and then find max "Value" for different "Codes". How can I do this - I would like to supply a condition to MAX() but I don't think that is possible.
Columns:
Date: date
Time: time
Value: float
Code: varchar
SELECT MAX([Value] where [Code]='GOLD') AS BestGold,
MAX([Value] where [Code]='SILVER') AS BestSilver
FROM [MyTable]
GROUP BY [Date]
Use a CASE expression:
SELECT
MAX(CASE WHEN [Code]='GOLD' THEN ['Value'] END) AS BestGold,
MAX(CASE WHEN [Code]='SILVER' THEN ['Value'] END) AS BestSilver
FROM [MyTable]
GROUP BY [Date];
The idea here is that the MAX function will only consider values of records for each respective type of code.
You can use inline iif:
SELECT MAX(IIF([Code]='GOLD', [Value], null)) AS BestGold,
MAX(IIF([Code]='SILVER', [Value], null)) as BestSilver
FROM [MyTable]
GROUP BY [Date]
or case:
SELECT MAX(case [Code] when 'GOLD' then [Value] end) AS BestGold,
MAX(case [Code] when 'SILVER' then [Value] end) as BestSilver
FROM [MyTable]
GROUP BY [Date]
Instead, use this
SELECT
[Date],
[Code],
MAX([Value]) AS Best
FROM [MyTable]
where cODE IN ('GOLD','SILVER')
GROUP BY [Date]
If you want it as Separate Column, Then try Pivoting the same
;WITH CTE
AS
(
SELECT
[Date],
[Code],
MAX([Value]) AS Best
FROM [MyTable]
where cODE IN ('GOLD','SILVER')
GROUP BY [Date]
)
SELECT
[Date],
BestGold = [GOLD],
BestSilver = [Silver]
FROM CTE
PIVOT
(
MAX(Best)
FOR
Code IN
(
[GOLD],[SILVER]
)
)P
You could use case;
SELECT
MAX(CASE WHEN Code='GOLD' THEN [VALUE] ELSE -1 END) AS BestGold,
MAX(CASE WHEN Code='SILVER' THEN [VALUE] ELSE -1 END) AS BestSilver
FROM [MyTable]
GROUP BY [Date];
I believe you require something like the below :-
DECLARE #TestCodes Table
(
Date date,
Time time,
Value float,
Code varchar(10)
)
INSERT INTO #TestCodes
VALUES
('2017-08-09','12:00',19900,'Gold'),
('2017-08-09','12:00',15001,'Gold'),
('2017-08-09','12:00',2500,'Gold'),
('2017-08-09','12:00',1200.01,'Metal'),
('2017-08-09','12:00',1900,'Metal'),
('2017-08-09','12:00',1800.1,'Silver'),
('2017-08-09','12:00',1100.01,'Silver'),
('2017-08-09','12:00',100.11,'Silver')
SELECT Date,Code,Max(value) AS MAXPriceOnAnyDate
FROM #TestCodes
GROUP BY [Date],CODE

SQL Where Clause Not Returning Correct Results

SELECT * FROM [dbo].[_5200_Sanoma]
WHERE right(left([VARIABLE1)],4),2) = 'RI'
and (
([Year] = '2014' and [Period] in('10','11','12'))
or [Year] = '2015')
or (
[Year] = '2016'
and [Period] in('01','02','03','04','05','06','07','08')--,'09','10','11','12')
)
and ([VARIABLE2] IN(
'String1',
'String2',
'String3',
'String4',
'String5',
'String6',
))
I had to change a few things to be more general but for some reason the first where clause, the right(left([VARIABLE1)],4),2) = 'RI' isn't working because I'm getting back results where that is other two character strings.
This query has worked before I added the last where clause condition
and ([VARIABLE2] IN(
'String1',
'String2',
'String3',
'String4',
'String5',
'String6',
))
So now that I added that it's not working. Any ideas?
Try to split your statements in different lines; you probably meant to do this:
SELECT * FROM [dbo].[_5200_Sanoma]
WHERE right(left([VARIABLE1)],4),2) = 'RI' AND
(
([Year] = '2014' and [Period] in('10','11','12')) OR
([Year] = '2015') OR
([Year] = '2016' and [Period] in('01','02','03','04','05','06','07','08'))
)
and ([VARIABLE2] IN ('String1','String2','String3','String4','String5','String6'))
Guess you just have some parenthesis problems with your or clauses (One of them is not included in the AND part after WHERE right(left([VARIABLE1)],4),2) = 'RI'
Try
and (
([Year] = '2014' and [Period] in('10','11','12')) or
[Year] = '2015' or
([Year] = '2016' and [Period] in('01','02','03','04','05','06','07','08')--,'09','10','11','12')
)

Calculating closing (sums of two views, including the past)

I am trying to get the remaining number of working units for each month, of a sum between a bought number of working unit, and a consumed number of working unit.
I tried two possibilities, but both have flaws :
In the first test, I created a "Months" table that contains every month and every year, in order to show all months in the final matrix I wish to create with these data. With this one, I get the closing whenever there is a consumed working unit, but when there is not, the column is "empty", because it does not get the last closing.
USE OTRS_Revised
SELECT [Customer], CASE WHEN [Year] < 2016 THEN 1 ELSE [Year] END AS [Year], CASE WHEN [Year] < 2016 THEN 0 ELSE [Month] END AS [Month], [Closing] AS Total, SUM([Closing])
OVER (PARTITION BY [Customer] ORDER BY [Year], [Month] ROWS UNBOUNDED PRECEDING) AS Closing
FROM [dbo].[WU_Closing_View]
WHERE [Customer] IN ('CustomerList')
GROUP BY [Customer], [Year], [Month], [Closing]
UNION ALL
SELECT '' AS Customer, CASE WHEN [Year] < 2016 THEN 1 ELSE [Year] END AS [Year], CASE WHEN [Year] < 2016 THEN 0 ELSE [Month] END AS [Month], '' AS Total, '' AS Sum_bought
FROM [dbo].Months
WHERE [Year] <= 2016
GROUP BY Year, Month
ORDER BY Customer, Year, Month
I also tried to do it "month by month", with the below query. It works for one month, but I can't find any way to use this to get the results for each month of the year 2016.
SELECT
(SELECT SUM(Closing) AS Expr1
FROM OTRS_Revised.dbo.WU_Bought_View
WHERE (Customer LIKE 'SomeCustomer') AND (DATEADD(Year, Year - 1900, DATEADD(Month, Month - 1, DATEADD(day, 0, 0))) <= DATEADD(Year, 2016 - 1900, DATEADD(Month, 5 - 1, DATEADD(day, 0, 0))))
GROUP BY Customer)
+
(SELECT SUM(Closing) AS Expr1
FROM OTRS_Revised.dbo.WU_Consumed_View
WHERE (Customer LIKE 'SomeCustomer') AND (DATEADD(Year, Year - 1900, DATEADD(Month, Month - 1, DATEADD(day, 0, 0))) <= DATEADD(Year, 2016 - 1900, DATEADD(Month, 5 - 1, DATEADD(day, 0, 0))))
GROUP BY Customer) AS Expr1,
[Month]
FROM OTRS_Revised.dbo.Months
GROUP BY [Month]
SELECT b.*
FROM
( SELECT CASE WHEN [Year] < 2016 THEN 1 ELSE [Year] END AS [Year], CASE WHEN [Year] < 2016 THEN 0 ELSE [Month] END AS [Month]
FROM [dbo].Months
WHERE [Year] <= 2016
GROUP BY Year, Month
ORDER BY Customer, Year, Month ) AS a
LEFT OUTER JOIN
(SELECT [Customer], CASE WHEN [Year] < 2016 THEN 1 ELSE [Year] END AS [Year], CASE WHEN [Year] < 2016 THEN 0 ELSE [Month] END AS [Month], [Closing] AS Total, SUM([Closing])
OVER (PARTITION BY [Customer] ORDER BY [Year], [Month] ROWS UNBOUNDED PRECEDING) AS Closing
FROM [dbo].[WU_Closing_View]
WHERE [Customer] IN ('CustomerList')
GROUP BY [Customer], [Year], [Month], [Closing]) AS b
ON a.Month = b.Month )
In your approach when you do union the rows that don't have matching months is getting removed. Since you want the months that does not have closing match as well, you need to use the left outer join
Something like this perhaps
DECLARE #T TABLE (ID INT, ProductID INT, TrDate DATE,InOut VARCHAR(10),Amount INT)
INSERT INTO #T VALUES
(1 ,1, '2016-01-01', 'I', 100),
(2 ,2, '2016-01-01', 'I', 100),
(3 ,3, '2016-02-01', 'I', 100),
(4 ,4, '2016-03-01', 'I', 100),
(5 ,1, '2016-03-01', 'I', 100),
(6 ,2, '2016-04-01', 'O', 10),
(7 ,3, '2016-05-01', 'I', 100),
(8 ,5, '2016-05-01', 'I', 100),
(9 ,5, '2016-05-01', 'O', 100),
(10 ,6, '2016-05-01', 'I', 100)
declare #m table (id int, menddate date)
insert #m values
(1,'2015-12-31'),(2,'2016-01-31'),(3,'2016-02-29'),(4,'2016-03-31'),
(5,'2016-04-30'),(6,'2016-05-31'),(7,'2016-06-30'),(4,'2016-07-31')
Select *
from
(
select -- t.*
x.xproductid , x.xyyyymm,
SUM(t.total) OVER (partition by x.xproductid
ORDER BY x.xyyyymm
ROWS UNBOUNDED PRECEDING) AS CumulativeTotal
from
(
SELECT t.ProductID tproductid, year(t.trdate) * 100 + month(t.trdate) tyyyymm,
sum(case when t.Inout = 'I' then t.Amount else t.amount * -1 end) as total
FROM #T t
group by ProductID, year(t.trdate) * 100 + month(t.trdate)
) t
right outer join
(select distinct productid as xproductid,year(m.menddate) * 100 + month(m.menddate) xyyyymm from #t t, #m m) x on x.xproductid = t.tproductid and x.xyyyymm = t.tyyyymm
) z
where z.xyyyymm >= 201601
order by z.xProductID,z.xyyyymm
Note the use of a right outer join to get all the month ends for all products

How to create dates from date components in SQL (T-SQL)?

How can I construct native date data type values in SQL (T-SQL)?
I've added some examples, but please provide your own. My examples assume that the month and year are being stored (or are readily available) as integer values, but maybe your example will assume that the day and the month (or whatever) are stored as text. I can't see the future; surprise me.
SELECT DATEFROMPARTS(#Year, #Month, #Day)
(From SQL Server 2012)
Why, with input data as strings one of the most obvious (and therefore hardly surprising, sorry) solutions would be:
SELECT
mydate = CAST([year] + RIGHT('0' + [month], 2) + '01' AS datetime)
/* or 'AS date' in SQL Server 2008+ */
FROM (
SELECT [month] = '2', [year] = '2011' UNION ALL
SELECT [month] = '03', [year] = '2011' UNION ALL
SELECT [month] = '5', [year] = '2011' UNION ALL
SELECT [month] = '12', [year] = '2011' UNION ALL
SELECT [month] = '8', [year] = '2084' UNION ALL
SELECT [month] = '1', [year] = '1940'
) x;
The following code shows how to create date values from year and month (integer) values:
SELECT DATEADD(
month,
DATEDIFF( month, 0, GETDATE() )
+ x.[month]
- MONTH( GETDATE() ),
DATEADD(
year,
DATEDIFF( year, 0, GETDATE() )
+ x.[year]
- YEAR( GETDATE() ),
0 ) )
FROM ( SELECT [month] = 2, [year] = 2011
UNION ALL
SELECT [month] = 3, [year] = 2011
) x;
Date values from year, month, AND day (integer) values, though maybe the inputs should be sanitized first:
SELECT DATEADD(
day,
x.[day] - DAY(0),
DATEADD(
month,
x.[month] - MONTH(0),
DATEADD(
year,
x.[year] - YEAR(0),
0 ) ) )
FROM ( SELECT [month] = 2, [year] = 2011, [day] = 14
UNION ALL
SELECT [month] = 3, [year] = 2011, [day] = 2
UNION ALL
SELECT [month] = 5, [year] = 2011, [day] = 1
UNION ALL
SELECT [month] = 7, [year] = 2011, [day] = 0
UNION ALL
SELECT [month] = 8, [year] = 2084, [day] = 40
UNION ALL
SELECT [month] = 1, [year] = 1940, [day] = -6
) x;
More example code to create date values from year and month (integer) values, but even simpler than some other example code:
SELECT DATEADD(
month,
x.[month] - MONTH(0),
DATEADD(
year,
x.[year] - YEAR(0),
0 ) )
FROM ( SELECT [month] = 2, [year] = 2011
UNION ALL
SELECT [month] = 3, [year] = 2011
UNION ALL
SELECT [month] = 5, [year] = 2011
UNION ALL
SELECT [month] = 7, [year] = 2011
UNION ALL
SELECT [month] = 8, [year] = 2084
UNION ALL
SELECT [month] = 1, [year] = 1940
) x;