Creating conditional column in SQL - tsql

I am trying to create a conditional column in T-SQL like an if statement but rather than printing a string I would like it to pull through data from another column in the data set.
SELECT
CASE
WHEN NameDay = 'Sunday'
THEN [sales]
ELSE [SunAdj]
END AS AdjustedDays
FROM
Table
Basically I want it to pull data from the [sales] column if the day in the NameDay column is 'Sunday' otherwise pull it from [SunAdj]. I can get it to work populating a string but not data from other columns. Is this possible? If so how? Any help would be much appreciated.
Product category Sales SunAdj NameDay
--------------------------------------------------
Meat 1000 3500 Sunday
Fish 1000 3500 Wednesday
Bakery 1000 3500 Friday
I would want another column called AdjustedDays showing 1000 for the first row and 3500 for the others.

This is a quick CTE setup of the data in the question:
; with sourceData (Product, Category, Sales, SunAdj, NameDay)
as
(
select 'Meat', null, 1000, 3500, 'Sunday'
union select 'Fish', null, 1000, 3500, 'Wednesday'
union select 'Bakery', null, 1000, 3500, 'Friday'
)
...
and we can use it to show that your statement is already correct:
; with sourceData (Product, Category, Sales, SunAdj, NameDay)
as
(
select 'Meat', null, 1000, 3500, 'Sunday'
union select 'Fish', null, 1000, 3500, 'Wednesday'
union select 'Bakery', null, 1000, 3500, 'Friday'
)
select
Product, Category, Sales, SunAdj, NameDay,
CASE when NameDay = 'Sunday' then [sales]
else [SunAdj] end as AdjustedDays
from sourceData
Just to add a little value to my answer, you can achieve the same thing with the IIF function:
; with sourceData (Product, Category, Sales, SunAdj, NameDay)
as
(
select 'Meat', null, 1000, 3500, 'Sunday'
union select 'Fish', null, 1000, 3500, 'Wednesday'
union select 'Bakery', null, 1000, 3500, 'Friday'
)
select
Product, Category, Sales, SunAdj, NameDay, iif(NameDay='Sunday', SunAdj, Sales) AdjustedDays
from sourceData
Both queries give the same result:
Product Category Sales SunAdj NameDay AdjustedDays
------- ----------- ----------- ----------- --------- ------------
Bakery NULL 1000 3500 Friday 1000
Fish NULL 1000 3500 Wednesday 1000
Meat NULL 1000 3500 Sunday 3500

Related

Oracle SQL return value from child table with minimum row number with values in specific list

I have a need to select all rows from a table (main table) and join to another table (child table). In the results set, I want to include one column from the child table, that is only the first row / line number with a column value in a specified list. If there is no match for the specified list, it should be (null)
Desired Result:
ORDER_NO
ORDER_DATE
ORDER CUST
ORDER_VALUE
ITEM
1
02/14/2022
12345
$1,000.00
APPLES
2
02/13/2022
67890
$5,000.00
(null)
3
02/12/2022
45678
$100.00
PEARS
Example:
Main Table: Order Table
Order Number (Handle)
Order Date,
Order Customer,
Order Value
ORDER_NO
ORDER_DATE
ORDER CUST
ORDER_VALUE
1
02/14/2022
12345
$1,000.00
2
02/13/2022
67890
$5,000.00
3
02/12/2022
45678
$100.00
Child Table: Order Details Tbl
Order Number (Handle)
Line Number = Order Line No
Ordered Item,
Ordered Qty
ORDER_NO
LINE_NO
ITEM
1
10
APPLES
1
20
ORANGES
1
30
LETTUCE
2
10
BROCCOLI
2
20
CAULIFLOWER
2
30
LETTUCE
3
10
KALE
3
20
RADISHES
3
30
PEARS
In this example, the returned column is essentially the first line of the order that is a fruit, not a vegetable. And if the order includes no matching fruit, null is returned.
What my code is thus far:
SELECT
MAIN.ORDER_NO,
MAIN.ORDER_DATE,
MAIN.ORDER_CUST,
MAIN.ORDER_VALUE,
B.ITEM
FROM
MAIN
LEFT JOIN
(
SELECT
CHILD.ORDER_NO,
CHILD.LINE_NO,
CHILD.ITEM
FROM
CHILD
WHERE
CHILD.ORDER_NO||'_'||LINE_NO IN
(
SELECT
CHILD.ORDER_NO||'_'||MIN(LINE_NO) AS ORDER_LINE_NO
FROM
CHILD
WHERE
CHILD.ITEM IN ('APPLES','ORANGES','PEACHES','PEARS','GRAPES')
GROUP BY
CHILD.ORDER_NO
)
) B ON MAIN.ORDER_NO = B.ORDER_NO
'''
This code is of course not working as desired, as table 'B' is including all results from CHILD.
From Oracle 12, you can use:
SELECT o.*,
d.item
FROM orders o
LEFT OUTER JOIN LATERAL(
SELECT *
FROM order_details d
WHERE o.order_no = d.order_no
AND item IN ('APPLES','ORANGES','PEACHES','PEARS','GRAPES')
ORDER BY line_no ASC
FETCH FIRST ROW ONLY
) d
ON (1 = 1)
In earlier versions you can use:
SELECT o.*,
d.item
FROM orders o
LEFT OUTER JOIN(
SELECT d.*,
ROW_NUMBER() OVER (PARTITION BY order_no ORDER BY line_no ASC)
AS rn
FROM order_details d
WHERE item IN ('APPLES','ORANGES','PEACHES','PEARS','GRAPES')
) d
ON (o.order_no = d.order_no AND rn = 1)
Which, for the sample data:
CREATE TABLE orders (ORDER_NO, ORDER_DATE, ORDER_CUST, ORDER_VALUE) AS
SELECT 1, DATE '2022-02-14', 12345, 1000.00 FROM DUAL UNION ALL
SELECT 2, DATE '2022-02-13', 67890, 5000.00 FROM DUAL UNION ALL
SELECT 3, DATE '2022-02-12', 45678, 100.00 FROM DUAL;
CREATE TABLE Order_Details (ORDER_NO, LINE_NO, ITEM) AS
SELECT 1, 10, 'APPLES' FROM DUAL UNION ALL
SELECT 1, 20, 'ORANGES' FROM DUAL UNION ALL
SELECT 1, 30, 'LETTUCE' FROM DUAL UNION ALL
SELECT 2, 10, 'BROCCOLI' FROM DUAL UNION ALL
SELECT 2, 20, 'CAULIFLOWER' FROM DUAL UNION ALL
SELECT 2, 30, 'LETTUCE' FROM DUAL UNION ALL
SELECT 3, 10, 'KALE' FROM DUAL UNION ALL
SELECT 3, 20, 'RADISHES' FROM DUAL UNION ALL
SELECT 3, 30, 'PEARS' FROM DUAL;
Both output:
ORDER_NO
ORDER_DATE
ORDER_CUST
ORDER_VALUE
ITEM
1
2022-02-14 00:00:00
12345
1000
APPLES
2
2022-02-13 00:00:00
67890
5000
null
3
2022-02-12 00:00:00
45678
100
PEARS
db<>fiddle here

Show every week of the Year even if there is no data

I have query that pulls data by week and groups it together. But i does not display weeks that doesn't have any data. I want show all weeks even if they don't have data as null maybe
Here is the query if someone can help me with this it will awesome
SELECT
DATEADD (week, datediff(week, 0, StartDate), -1) as 'WeekOf'
,DATEADD (week, datediff(week, 0, StartDate), +5) as 'to'
,DATEPART(wk, StartDate) as 'WeekNumber'
FROM [DESOutage].[dbo].[OPSInterruption]
Where StartDate > '2020-01-01' and EndDate <'2020-02-01'
Group by DATEADD (week, datediff(week, 0, StartDate), -1),DATEPART(wk, StartDate),DATEADD (week, datediff(week, 0, StartDate), +5)
***************Output***************
As you could see week 2 and 4 is missing out since there is no data being returned. I would still like to see week 2 and 4 in the output with maybe 0 as result.
WeekOf to WeekNumber
2019-12-29 00:00:00.000 2020-01-04 00:00:00.000 1
2020-01-12 00:00:00.000 2020-01-18 00:00:00.000 3
2020-01-26 00:00:00.000 2020-02-01 00:00:00.000 5
You probably need a calendar table. Here is a quick way of generating one, with an untested implementation of your code. I am assuming that the StartDate may contain a time component thus the need to coalesce the dates.
DECLARE #StartYear DATETIME = '20200101'
DECLARE #days INT = 366
;WITH
E1(N) AS (
SELECT 1 UNION ALL SELECT 1 UNION ALL SELECT 1 UNION ALL
SELECT 1 UNION ALL SELECT 1 UNION ALL SELECT 1 UNION ALL
SELECT 1 UNION ALL SELECT 1 UNION ALL SELECT 1 UNION ALL SELECT 1
), -- 1*10^1 or 10 rows
E2(N) AS (SELECT 1 FROM E1 a, E1 b), -- 1*10^2 or 100 rows
E4(N) AS (SELECT 1 FROM E2 a, E2 b), -- 1*10^4 or 10,000 rows
E8(N) AS (SELECT 1 FROM E4 a, E4 b), -- 1*10^8 or 100,000,000 rows
Tally(N) AS (SELECT TOP (#Days) ROW_NUMBER() OVER (ORDER BY (SELECT NULL)) FROM E8),
Calendar AS (
SELECT StartOfDay = DATEADD(dd,N-1,#StartYear),
EndOfDay = DATEADD(second, -1, DATEADD(dd,N ,#StartYear))
FROM Tally)
SELECT DATEADD (week, datediff(week, 0, COALESCE(x.StartDate, c.StartOfDay) ), -1) as 'WeekOf'
, DATEADD (week, datediff(week, 0, COALESCE(x.StartDate, c.StartOfDay)), +5) as 'to'
, DATEPART(wk, COALESCE(x.StartDate, c.StartOfDay)) as 'WeekNumber'
FROM Calendar c
INNER JOIN [DESOutage].[dbo].[OPSInterruption] x
ON x.StartDate > c.StartOfDay AND x.StartDate <= c.EndOfDay
WHERE c.StartOfDay > '2020-01-01' AND c.StartOfDay <'2020-02-01'
GROUP BY DATEADD (week, datediff(week, 0, COALESCE(x.StartDate, c.StartOfDay)), -1),
DATEPART(wk, COALESCE(x.StartDate, c.StartOfDay)),
DATEADD (week, datediff(week, 0, COALESCE(x.StartDate, c.StartOfDay)), +5)

How can I remove the null values and make it to 10 rows in Postgresql?

I am new to Postgresql. I have a table called 'sales'.
create table sales
(
cust varchar(20),
prod varchar(20),
day integer,
month integer,
year integer,
state char(2),
quant integer
)
insert into sales values ('Bloom', 'Pepsi', 2, 12, 2001, 'NY', 4232);
insert into sales values ('Knuth', 'Bread', 23, 5, 2005, 'PA', 4167);
insert into sales values ('Emily', 'Pepsi', 22, 1, 2006, 'CT', 4404);
insert into sales values ('Emily', 'Fruits', 11, 1, 2000, 'NJ', 4369);
insert into sales values ('Helen', 'Milk', 7, 11, 2006, 'CT', 210);
insert into sales values ('Emily', 'Soap', 2, 4, 2002, 'CT', 2549);
something like this:
Now I want to find the “most favorable” month (when most amount of the product was
sold) and the “least favorable” month (when the least amount of the product was sold) for each product.
The result should be like this:
I entered
SELECT
prod product,
MAX(CASE WHEN rn2 = 1 THEN month END) MOST_FAV_MO,
MAX(CASE WHEN rn1 = 1 THEN month END) LEAST_FAV_MO
FROM (
SELECT
*,
ROW_NUMBER() OVER(PARTITION BY prod ORDER BY quant ) rn1,
ROW_NUMBER() OVER(PARTITION BY prod ORDER BY quant DESC) rn2
FROM sales
) x
WHERE rn1 = 1 or rn2 = 1
GROUP BY prod,quant;
Then there are null values for each product and there are 20 rows in total:
So how can I remove the null values in these rows and make the total number of rows to 10 (There are 10 distinct products in total)???
I would say that the GROUP BY clause should be
GROUP BY prod
Otherwise you get one line per different quant, which is not what you want.

DateDiff Rows Where UserID is a match

On Sql Server 2012 (T-SQL), I would like to analyse the date difference between the end dates and start dates for the same userid, and to see if there is a equal or greater than twelve month gap between times.
So for which ContractID the start date is =>12m than the previous end date.
ContractID UserID StartDate EndDate 12m Lapse
1 779 01/01/2000 01/01/2010 False
2 779 01/01/2010 01/01/2015 False
3 779 01/01/2016 NULL True
4 1021 09/03/2008 NULL False
Things perhaps to note are the userID is not in order on the real table, only the contractID is.
Using a CTE and the LAG() window function it's quite easy:
Create sample data:
DECLARE #T as table
(
ContractID int,
UserID int,
StartDate date,
EndDate date
)
INSERT INTO #T VALUES
(1, 779, '01/01/2000', '01/01/2010'),
(2, 779, '01/01/2010', '01/01/2015'),
(3, 779, '01/01/2016', NULL),
(4, 1021, '09/03/2008', NULL)
The query:
;WITH CTE AS
(
SELECT ContractID,
UserID,
StartDate,
EndDate,
LAG(EndDate) OVER(PARTITION BY UserId ORDER BY StartDate) As PreviousEndDate
FROM #T
)
SELECT ContractID,
UserID,
StartDate,
EndDate,
CASE WHEN DATEDIFF(MONTH, ISNULL(PreviousEndDate, StartDate), StartDate) >= 12 THEN
'True'
ELSE
'False'
END As '12m Lapse'
FROM CTE
Results:
ContractID UserID StartDate EndDate 12m Lapse
----------- ----------- ---------- ---------- ---------
1 779 2000-01-01 2010-01-01 False
2 779 2010-01-01 2015-01-01 False
3 779 2016-01-01 NULL True
4 1021 2008-09-03 NULL False
SELECT * FROM Table WHERE DATEDIFF(M,StartDate,EndDate) >=12
Starting with SQL Server 2012, there is a function called Lag that will help you get what you need.
The partition by of the window function will make sure that its separated by userID, and the order by will make sure its in ContractID order.
with prevEndDate as
(
select t.contractID
, t.userID
, t.startDate
, t.endDate
, lag(t.endDate,1,NULL) over (partition by t.userID order by t.contractID asc) as prevEndDate
from db_name.dbo.myTable as t
)
select p.contractID
, p.userID
, p.startDate
, p.endDate
, case when datediff(m,p.prevEndDate, p.startDate) >= 12 then 'True' else 'False' end as [12m Lapse]
from prevEndDate as p

Merge records that meet a particular criteria - TSQL

SQL Server 2008
Hi
I have an issue where i have two of the same accounts which i would like to merge together, the list looks like below:
AcctID AcctType AcctSubType Curr TransType Amount
1 CCY SET EUR Opening 1000
1 CCY SET EUR BUY -100
1 CCY SET EUR SEL 100
1 CCY SET EUR Closing 1000
2 CCY SET EUR Opening 2000
2 CCY SET EUR SEL 100
2 CCY SET EUR Closing 2100
3 CCY INC EUR Opening 1000
3 CCY INC EUR SEL 200
3 CCY INC EUR BUY -100
3 CCY INC EUR Closing 1100
So basically i would like to merge it so it looks like this:
AcctID AcctType AcctSubType Curr TransType Amount
1 CCY SET EUR Opening 3000
1 CCY SET EUR BUY -100
1 CCY SET EUR SEL 100
2 CCY SET EUR SEL 100
1 CCY SET EUR Closing 3100
3 CCY INC EUR Opening 1000
3 CCY INC EUR SEL 200
3 CCY INC EUR BUY -100
3 CCY INC EUR Closing 1100
However i can't think of the best way to code this in SQL in my stored proc. I can only really think of a merge or a union, but i only want it to merge where there are two capital accounts, a capital account is signified by the 'CCY' AcctType and AcctSubType 'SET'.
A point in the right direction would be much appreciated.
Thanks
EDIT: Actual stored proc
CREATE TABLE #Workbook
(ID INT
,PortfolioID VARCHAR(20)
,PortfolioName VARCHAR(255)
,InstrumentID VARCHAR(20)
, IssueID VARCHAR(50)
, CashAccountInstrumentID VARCHAR(20)
, CashAccountName VARCHAR(255)
, CashAccountType CHAR(3)
, CashAccountSubType CHAR(3)
, TransactionClass VARCHAR(10)
, TransactionType VARCHAR(20)
, EffPostDate DATETIME
, TradeDate DATETIME
, SettleDate DATETIME
, CcySettle VARCHAR(3)
, DateItem DATETIME
, RunningTotalDate DATETIME
, Detail VARCHAR(300)
, ProceedsLocal FLOAT
, Total FLOAT
, RunningTotal FLOAT
, ReconValue FLOAT
, ReconNotes VARCHAR(50)
, RecordType VARCHAR(1)
, Sequence INT
, RecordNum INT
, TransactionID VARCHAR(20)
)
INSERT INTO #Workbook
EXEC dbo.usp_Generic_ReconCashAccount_NEW '2010-10-01', '2010-10- 07', '', 'OOGENHF', 'SS','','','','',''
DELETE FROM #Workbook
WHERE TransactionType = 'BBA'
OR (ProceedsLocal = 0 AND RecordType = 'T')
;;with needed as
(
select * from #Workbook w
WHERE w.CashAccountType = 'CCY'
AND w.CashAccountSubType = 'SET'
AND w.TransactionType In ('Opening','Closing')
AND w.CcySettle = w.CcySettle
)
select
min(ID)
, PortfolioID
, PortfolioName
, InstrumentID
, IssueID
, min(CashAccountInstrumentID) as CashAccountInstrumentID
, min(CashAccountName) as CashAccountName
, CashAccountType
, CashAccountSubType
, TransactionClass
, TransactionType
, EffPostDate
, TradeDate
, SettleDate
, CcySettle
, DateItem
, RunningTotalDate
, Detail
, sum(ProceedsLocal) as ProceedsLocal
, sum(Total) as Total
, sum(RunningTotal) as RunningTotal
, sum(ReconValue) as ReconValue
, ReconNotes
, RecordType
, Sequence
, RecordNum
, TransactionID
from needed
group by ID,PortfolioID,PortfolioName,InstrumentID,IssueID,CashAccountInstrumentID,CashAccountName,CashAccountType,CashAccountSubType,TransactionClass,TransactionType,EffPostDate,TradeDate,SettleDate
,CcySettle, DateItem,RunningTotalDate,Detail,ProceedsLocal,Total,RunningTotal,ReconValue,ReconNotes,RecordType,Sequence,RecordNum,TransactionID
UNION ALL
(
select * from #Workbook
except
select * from needed
)
drop table #Workbook
How about this:
;with test (AcctID, AcctType, AcctSubType, Curr, TransType, Amount)
as
(
select 1, 'CCY', 'SET', 'EUR', 'Opening', 1000
union all
select 1, 'CCY', 'SET', 'EUR', 'BUY', -100
union all
select 1, 'CCY', 'SET', 'EUR', 'SEL', 100
union all
select 1, 'CCY', 'SET', 'EUR', 'Closing', 1000
union all
select 2, 'CCY', 'SET', 'EUR', 'Opening', 2000
union all
select 2, 'CCY', 'SET', 'EUR', 'SEL', 100
union all
select 2, 'CCY', 'SET', 'EUR', 'Closing', 2100
union all
select 3, 'CCY', 'INC', 'EUR', 'Opening', 1000
union all
select 3, 'CCY', 'INC', 'EUR', 'SEL', 200
union all
select 3, 'CCY', 'INC', 'EUR', 'BUY', -100
union all
select 3, 'CCY', 'INC', 'EUR', 'Closing', 1100
union all
select 4, 'CCY', 'SET', 'SEK', 'Opening', 2000
union all
select 4, 'CCY', 'SET', 'SEK', 'SEL', 100
union all
select 4, 'CCY', 'SET', 'SEK', 'Closing', 2100
)
, needed as
(
select *
from test
where AcctType = 'CCY'
and AcctSubType = 'SET'
and TransType in ('Opening','Closing')
)
select min(AcctID) as AcctID
,AcctType
,AcctSubType
,Curr
,TransType
,sum(Amount) as Amount
from needed
group by
AcctType
,AcctSubType
,Curr
,TransType
union all
(
select *
from test
except
select *
from needed
)
EDIT: Updated to support multiple currencys.
The only change is to wrap the final SELECT - EXCEPT in parantheses.