I am trying to determine if something is possible to do in SQL where I am creating a view for users and I need to create a column that would state whether or not a line number is "Open" or "Closed". The trouble is that the process to determine that value is based on multiple factors/values of other columns from the source table. Take a look at some data that the view currently generates.
Line # Req_Qty Rej_Qty Adj_Qty Alt_Qty Shipped Cxl Rec Conf
Line 1 71.00 0.00 100.00 0.00 100.00 0.00 100.00 100.00
Line 2 23.00 0.00 0.00 0.00 23.00 0.00 0.00 0.00
Line 3 11.00 0.00 10.00 0.00 10.00 0.00 0.00 0.00
Line 4 12.00 12.00 0.00 0.00 0.00 0.00 0.00 0.00
Line 5 0.00 0.00 0.00 20.00 20.00 0.00 0.00 0.00
In the table above the way the determination is made is as follows:
If there is any value > 0 in Adj_Qty, then the Adj_Qty(Adjusted Qty) effectively becomes the Req_Qty(Requested Qty). If both the Req_Qty and the Adj_Qty are 0 and the Alt_Qty >0, then that now becomes the real Req_ Quantity. So basically, my first comparison is Adj->Req->Alt. Whatever that quantity is would then be reduced by the Rej_Qty(Rejected) or the Cxl_Qty(Cancelled) which results in a balance.
Finally, if the quantity in either the Rec or Conf columns matches that balance, then the line # is closed. So, for the table, Lines 1 and 4 would be closed, 2, 3, and 5 would be open. Is there any way to create a field/column in SQL that would be able to assign "Open" or "Closed" based on that type of logic?
You can use the CASE clause to compute the balance and the status in the view. For example:
create view v as
select *,
case when (
case when adj_qty > 0 then adj_qty
when req_qty > 0 then req_qty
when alt_qty > 0 then alt_qty
else 0.0
end
- rej_qty
- cxl_qty
) in (rec, conf) then 'closed'
else 'open' end as status
from t
I probably didn't understand all the details of your specific logic but this query should be pretty close to what you need. Tweak as needed.
I have got this query
select
Id,
case when isThirdParty = 0 then sum(total) end as FirstPartyFees,
case when isThirdParty = 1 then sum(total) end as ThirdPartyFees
from
MyTable
group by
id, isThirdParty
And I got this result
Id FirstPartyFees ThirdPartyFees
------------------------------------ --------------------------------------- ---------------------------------------
DA29BDC0-BE3F-4193-BFDC-493B354CE368 15.00 0.00
2EF0B590-FE4F-42E8-8426-5864A739C16B 5.00 0.00
246DC3D8-732F-4AE3-99F3-BDEBF98F7719 15.00 0.00
FC81F220-ED54-48FE-AE1B-C394492E82A4 5.00 0.00
336D9CF1-6970-48BA-90E5-C7889914DDCB 114.00 0.00
6F2EEF6F-5FA1-42E5-A988-DB88037DAB92 5.00 0.00
80763B37-68E1-4716-B32A-FE82C1700B52 15.00 0.00
DA29BDC0-BE3F-4193-BFDC-493B354CE368 0.00 1.00
2EF0B590-FE4F-42E8-8426-5864A739C16B 0.00 1.00
246DC3D8-732F-4AE3-99F3-BDEBF98F7719 0.00 1.00
FC81F220-ED54-48FE-AE1B-C394492E82A4 0.00 1.00
336D9CF1-6970-48BA-90E5-C7889914DDCB 0.00 0.00
6F2EEF6F-5FA1-42E5-A988-DB88037DAB92 0.00 1.00
80763B37-68E1-4716-B32A-FE82C1700B52 0.00 1.00
As you see, I get duplicates for the first party and the third party. How can I group first party and third party total for the same Id in 1 row?
You are looking for conditional aggregation.
You should move the case expression inside the sum() and remove isThirdParty from the group by clause.
SELECT Id,
SUM(CASE WHEN isThirdParty = 0 THEN total END) AS FirstPartyFees,
SUM(CASE WHEN isThirdParty = 1 THEN total END) AS ThirdPartyFees
FROm MyTable
GROUP BY id
Because isThirdParty is an indicator, you can also just use math:
select Id, SUM(total * (1 - isThirdParty)) as FirstPartyFees,
SUM(total * isThirdParty) as ThirdPartyFees
frOm MyTable
group by id;
I have the following Table
Account Netflow FeeAmount Income TWR MarketValue Date
33L951572 0.00 0.00 0.00 0.00 375645.74 3/31/2004
33L951572 5547.31 0.00 0.00 0.08 338817.64 12/31/2004
33L951572 13250.45 0.00 35.00 0.01 322791.22 12/31/2005
33L951572 344.12 0.00 310.66 0.02 328899.02 1/31/2006
33L951572 6168.03 0.00 69.78 0.03 326221.04 2/28/2006
33L951572 140.50 0.00 186.62 0.01 328616.53 3/31/2006
I need this table to have a row for every month end and the date is always a month end date. However there are gaps in the dates. You can see for example 3/31/2004 jumps to 12/31/2014 then 12/31/2014 jumps to 12/31/2015, after which the data is monthly.
I want to insert a row with 0's in all rows. However I would also like to include the last known MarketValue whatever it may be before the gap.
So ideally this table would look as follows.
Account Netflow FeeAmount Income TWR MarketValue Date
33L951572 0.00 0.00 0.00 0.000 375,645.74 3/31/2004
33L951572 0.00 0.00 0.00 0.000 375,645.74 4/30/2004
33L951572 0.00 0.00 0.00 0.000 375,645.74 5/31/2004
33L951572 0.00 0.00 0.00 0.000 375,645.74 6/30/2004
33L951572 0.00 0.00 0.00 0.000 375,645.74 7/31/2004
33L951572 0.00 0.00 0.00 0.000 375,645.74 8/31/2004
33L951572 0.00 0.00 0.00 0.000 375,645.74 9/30/2004
33L951572 0.00 0.00 0.00 0.000 375,645.74 10/31/2004
33L951572 0.00 0.00 0.00 0.000 375,645.74 11/30/2004
33L951572 5,547.31 0.00 0.00 0.077 338,817.64 12/31/2004
33L951572 0.00 0.00 0.00 0.000 338,817.64 1/31/2005
33L951572 0.00 0.00 0.00 0.000 338,817.64 2/28/2005
33L951572 0.00 0.00 0.00 0.000 338,817.64 3/31/2005
33L951572 0.00 0.00 0.00 0.000 338,817.64 4/30/2005
33L951572 0.00 0.00 0.00 0.000 338,817.64 5/31/2005
33L951572 0.00 0.00 0.00 0.000 338,817.64 6/30/2005
33L951572 0.00 0.00 0.00 0.000 338,817.64 7/31/2005
33L951572 0.00 0.00 0.00 0.000 338,817.64 8/31/2005
33L951572 0.00 0.00 0.00 0.000 338,817.64 9/30/2005
33L951572 0.00 0.00 0.00 0.000 338,817.64 10/31/2005
33L951572 0.00 0.00 0.00 0.000 338,817.64 11/30/2005
33L951572 13,250.45 0.00 35.00 0.006 322,791.22 12/31/2005
33L951572 344.12 0.00 310.66 0.019 328,899.02 1/31/2006
33L951572 6,168.03 0.00 69.78 0.026 326,221.04 2/28/2006
33L951572 140.50 0.00 186.62 0.007 328,616.53 3/31/2006
The query provided below by Clockwork Muse works perfectly if there is only one attribute to perform the logic on. That attribute in the first example is Account.
However I've realized some of my data needs to be partitioned by a second criteria, AssetClassCode. Meaning within accounts there are sub attributes. Here is the example again but with the added attribute.
Account Netflow FeeAmount Income TWR AssetClassCode AssetClass MarketValue Date
33L951572 0 5 0 0.87947 1 Cash 1000 3/31/2004
33L951572 0 6 45 0.25564 2 Equity 2000 3/31/2004
33L951572 0 8 5 0.90677 3 Fixed 3000 3/31/2004
33L951572 123 5 2 0.29787 1 Cash 4000 7/30/2014
33L951572 456 4 4 0.55341 2 Equity 5000 7/30/2014
33L951572 657 2 45 0.10634 3 Fixed 6000 7/30/2014
Here is the desired result
Account Netflow FeeAmount Income TWR AssetClassCode AssetClass MarketValue Date
33L951572 0 5 0 0.88 1 Cash 1000 3/31/2004
33L951572 0 6 45 0.26 2 Equity 2000 3/31/2004
33L951572 0 8 5 0.91 3 Fixed 3000 3/31/2004
33L951572 0 0 0 0.00 1 Cash 1000 4/30/2014
33L951572 0 0 0 0.00 2 Equity 2000 4/30/2014
33L951572 0 0 0 0.00 3 Fixed 3000 4/30/2014
33L951572 0 0 0 0.00 1 Cash 1000 5/30/2014
33L951572 0 0 0 0.00 2 Equity 2000 5/30/2014
33L951572 0 0 0 0.00 3 Fixed 3000 5/30/2014
33L951572 0 0 0 0.00 1 Cash 1000 6/30/2014
33L951572 0 0 0 0.00 2 Equity 2000 6/30/2014
33L951572 0 0 0 0.00 3 Fixed 3000 6/30/2014
33L951572 123 5 2 0.30 1 Cash 4000 7/30/2014
33L951572 456 4 4 0.55 2 Equity 5000 7/30/2014
33L951572 657 2 45 0.11 3 Fixed 6000 7/30/2014
UPDATE
I'm getting redundant values. I made a new table called CAC_Codes that reflects what you have in AssetClass. The relevant tables are now FTDatelist as the calendar table. FTPerfCACCAssetClass which has the various measures, and CAC_Codes which has the asset clsas information.
SELECT Account.accountID,
COALESCE(FTPerfCACCAssetClass.AccountNetDeposits, 0) AS netFlow, COALESCE(FTPerfCACCAssetClass.AccountFees, 0) AS feeAmount,
COALESCE(FTPerfCACCAssetClass.AccountIncome, 0) AS income, COALESCE(FTPerfCACCAssetClass.AccountReturn, 0) AS TWR,
CAC_Codes.assetClassCode, CAC_Codes.assetClass,
MarketValue.AccountMKV,
Calendar.calendarDate
FROM (SELECT MAX(calendarDate) AS calendarDate
FROM FTDateList
GROUP BY calendarYear, calendarMonth) Calendar
CROSS JOIN (SELECT DISTINCT accountID
FROM FTPerfCACCAssetClass) Account
CROSS JOIN CAC_Codes
LEFT JOIN FTPerfCACCAssetClass
ON FTPerfCACCAssetClass.accountID = Account.accountID
AND FTPerfCACCAssetClass.assetClassCode = CAC_Codes.assetClassCode
AND FTPerfCACCAssetClass.EndDate = Calendar.calendarDate
JOIN (SELECT accountid, assetClassCode,
AccountMKV,
EndDate AS valueStartDate,
LEAD(EndDate, 1, DATEADD(day, 1, EndDate)) OVER (PARTITION BY accountid, assetClassCode ORDER BY EndDate) AS valueEndDate
FROM FTPerfCACCAssetClass) MarketValue
ON MarketValue.accountID = Account.accountID
AND MarketValue.assetClassCode = CAC_Codes.assetClassCode
AND Calendar.calendarDate >= MarketValue.valueStartDate
AND Calendar.calendarDate < MarketValue.valueEndDate
ORDER BY Account.accountID, Calendar.calendarDate, CAC_Codes.assetClassCode
However I'm getting results that look like this.
accountID netFlow feeAmount income TWR assetClassCode assetClass AccountMKV calendarDate
100106 11532813.47000000000 0.00000000000 0.00000000000 0.00000000000 36 Domestic Large Cap 11532813.48000000000 2007-03-31
100106 11532813.47000000000 0.00000000000 0.00000000000 0.00000000000 36 Domestic Large Cap 11532813.48000000000 2007-03-31
100106 11532813.47000000000 0.00000000000 0.00000000000 0.00000000000 36 Domestic Large Cap 11532813.48000000000 2007-03-31
100106 11532813.47000000000 0.00000000000 0.00000000000 0.00000000000 36 Domestic Large Cap 11532813.48000000000 2007-03-31
100106 3055.94000000000 0.00000000000 1.38000000000 -0.06492600000 1 Cash and Money Market 2857.53000000000 2007-04-30
100106 3055.94000000000 0.00000000000 1.38000000000 -0.06492600000 1 Cash and Money Market 2857.53000000000 2007-04-30
100106 3055.94000000000 0.00000000000 1.38000000000 -0.06492600000 1 Cash and Money Market 2857.53000000000 2007-04-30
100106 3055.94000000000 0.00000000000 1.38000000000 -0.06492600000 1 Cash and Money Market 2857.53000000000 2007-04-30
One big problem is that you actually want two different things on each date:
The "instant" value of the row (fee, income, etc).
The ongoing value of a column (market value).
Now that we know what we're looking for, we can construct our statement.
First, I'm going to assume that you have both a calendar table and an account table (or would be only interested in one account, and don't need the extra join). We'll need to deal with the calendar data a bit, but accounts should be fine as-is. These form the initial basis of the query:
SELECT Account.account,
-- instantaneous columns
-- ongoing columns
Calendar.calendarDate
FROM (SELECT MAX(calendarDate) AS calendarDate
FROM Calendar
GROUP BY calendarYear, calendarMonth) Calendar
CROSS JOIN Account
This gives us a list of all accounts with all dates. You can add restrictions as necessary - you probably have dates in the future, after all - but the important part is getting the max date of each month. (Personally, I probably would have gone for the first day of the month because it's far easier to index that, but this works) The resulting Calendar query table is likely to be pulled into memory - it's very small (12 rows a year!).
Next comes getting the "instantaneous" row. Now that we have our "base" data, a simple join suffices:
COALESCE(MarketData.netFlow, 0) AS netFlow, COALESCE(MarketData.feeAmount, 0) AS feeAmount,
COALESCE(MarketData.income, 0) AS income, COALESCE(MarketData.TWR, 0) AS TWR,
......
LEFT JOIN MarketData
ON MarketData.marketDate = Calendar.calendarDate
AND MarketData.account = Account.account
... so if we have a row there, then display it. When we don't have a row, the value is 0.
And lastly, we need the "ongoing" value. This we have to collect separately. Now, normally you want to use something like LAG(marketValue)... unfortunately, the join to our "base" tables gives us a bunch of rows where marketValue is null, so the windowing would return that instead of our "previous" value. We need to create a range-query table.
A range query table is where you have an upper and lower bound for a given key. In the case of dates (like all positive-range key values), this is lower-bound inclusive (>=) and upper-bound exclusive (<). Essentially, our upper-bound here is the first instant we have a new market value (the old one is superseded). This we can use LEAD(...) to get:
MarketValue.marketValue,
........
JOIN (SELECT account, marketValue,
marketDate AS valueStartDate,
LEAD(marketDate, 1, '99991231') OVER (PARTITION BY account ORDER BY marketDate) AS valueEndDate
FROM MarketData) MarketValue
ON Calendar.calendarDate >= MarketValue.valueStartDate
AND Calendar.calendarDate < MarketValue.valueEndDate
AND MarketValue.Account = Account.account
Our MarketValue inline query returns a table that looks something like this:
33L951572 | 375645.74 | 2004-03-31 | 2004-12-31
... that we can join to for each row. Note how the join condition is constructed - this makes it so that there isn't a conflict between "old" and "new" marketValues. On the last row, because LEAD(...) would return a null value, we return the "next" day; because (again) we use an exclusive upper-bound, this makes our last entry the last joinable row.
Putting it all together gives this:
SELECT Account.account,
COALESCE(MarketData.netFlow, 0) AS netFlow, COALESCE(MarketData.feeAmount, 0) AS feeAmount,
COALESCE(MarketData.income, 0) AS income, COALESCE(MarketData.TWR, 0) AS TWR,
MarketValue.marketValue,
Calendar.calendarDate
FROM (SELECT MAX(calendarDate) AS calendarDate
FROM Calendar
GROUP BY calendarYear, calendarMonth) Calendar
CROSS JOIN Account
LEFT JOIN MarketData
ON MarketData.marketDate = Calendar.calendarDate
AND MarketData.account = Account.account
JOIN (SELECT account, marketValue,
marketDate AS valueStartDate,
LEAD(marketDate, 1, DATEADD(day, 1, marketDate)) OVER (PARTITION BY account ORDER BY marketDate) AS valueEndDate
FROM MarketData) MarketValue
ON Calendar.calendarDate >= MarketValue.valueStartDate
AND Calendar.calendarDate < MarketValue.valueEndDate
AND MarketValue.Account = Account.account
ORDER BY Account.account, Calendar.calendarDate
SQL Fiddle Example
(don't forget the outer ORDER BY, or rows may appear where you least expect them!)
Modifying the query
For each additional criteria to partition, or "repeat" by, there are a few simple steps to take.
First, you need to add the "base" reference, to ensure all rows are present:
-- I'm assuming you have a code reference table.
-- Otherwise, create it like I did for the account table
CROSS JOIN AssetClass
Step 1b - use this base reference for the columns in the SELECT, and probably the ORDER BY as well.
Second, you need to add the extra key value to both "child" table join conditions:
-- Because asset-class - 'Cash', etc - are _dependent_ values,
-- we only need the code key in this case
AND MarketData.assetClassCode = AssetClass.assetClassCode
Lastly, you need to add the relevant column to the partitioning:
... OVER (PARTITION BY account, assetClassCode ORDER BY marketDate) ...
Resulting in:
SELECT Account.account,
COALESCE(MarketData.netFlow, 0) AS netFlow, COALESCE(MarketData.feeAmount, 0) AS feeAmount,
COALESCE(MarketData.income, 0) AS income, COALESCE(MarketData.TWR, 0) AS TWR,
AssetClass.assetClassCode, AssetClass.assetClass,
MarketValue.marketValue,
Calendar.calendarDate
FROM (SELECT MAX(calendarDate) AS calendarDate
FROM Calendar
GROUP BY calendarYear, calendarMonth) Calendar
CROSS JOIN Account
CROSS JOIN AssetClass
LEFT JOIN MarketData
ON MarketData.account = Account.account
AND MarketData.assetClassCode = AssetClass.assetClassCode
AND MarketData.marketDate = Calendar.calendarDate
JOIN (SELECT account, marketValue,
marketDate AS valueStartDate,
LEAD(marketDate, 1, DATEADD(day, 1, marketDate)) OVER (PARTITION BY account, assetClassCode ORDER BY marketDate) AS valueEndDate
FROM MarketData) MarketValue
ON MarketValue.Account = Account.account
AND MarketValue.assetClassCode = AssetClass.assetClassCode
AND Calendar.calendarDate >= MarketValue.valueStartDate
AND Calendar.calendarDate < MarketValue.valueEndDate
ORDER BY Account.account, Calendar.calendarDate, AssetClass.assetClassCode
SQL Fiddle Example
(Note that I've adjusted the ordering of the conditions in the JOIN and LEFT JOIN, to better reflect the "primary" keys used: account and asset class code)
You need a date or numbers table to fill in the gaps. I had a similar problem a while back. Please see https://dba.stackexchange.com/questions/86435/filling-in-date-holes-in-grouped-by-date-sql-data.
In your case, after selecting from the numbers/calendar table, you'll have to do a subquery in an ISNULL to get the most recent value. This can be very expensive. Something like this...
SELECT ...
ISNULL(t.TWR, 0) TWR,
ISNULL(t.MarketValue, (SELECT MarketValue FROM Table inner WHERE inner.Date <= t.Date ORDER BY t.Date DESC) MarketValue
FROM Calendar c WITH (NOLOCK)
LEFT JOIN Table t ON t.Date=c.Date
WHERE c.Date >= #StartDate AND c.Date < #EndDate
I'ved got a problem same as this but I am using Postgres.
Calculate balance with mysql
have a table which contains the following data:
ID In Out
1 100.00 0.00
2 10.00 0.00
3 0.00 70.00
4 5.00 0.00
5 0.00 60.00
6 20.00 0.00
Now I need a query which gives me the following result:
ID In Out Balance
1 100.00 0.00 100.00
2 10.00 0.00 110.00
3 0.00 70.00 40.00
4 5.00 0.00 45.00
5 0.00 60.00 -15.00
6 20.00 0.00 5.00
How best to handle "balance" calculation. I was told there is window function in postgres, how would this be done using postgres window functions ?
Thanks.
select t.*, sum("In"-"Out") over(order by id) as balance
from tbl t
order by id
Fiddle: http://sqlfiddle.com/#!15/97dc5/2/0
Consider changing your column names "In" / "Out" so that you don't need to put them in quotes. (They are reserved words)
If you wanted only one customer (customer_id = 2):
select t.*, sum("In"-"Out") over(order by id) as balance
from tbl t
where customer_id = 2
order by id
If your query were to span multiple customers and you wanted a running balance that RESTARTED with each customer, you could use:
select t.*, sum("In"-"Out") over( partition by customer_id
order by customer_id, id ) as balance_by_cust
from tbl t
order by customer_id, id
I am trying to get the next row's 'alignment' value and store it into the current row in a field called nextAlign. I have tried using grouping and also the unique identifier for this table is not consistent and doesn't always have an increment of 1. Here is some data:
type field starttop startleft length decimals alignment
-------------------------------------------------------------------------------------------
Text CUSTOMER DETAILS CONFIRMATION 13.00 38.00 30.00 0.00 Centre
Text Please spare a few minutes to 15.00 2.00 30.00 0.00 Left
Text confirm your details as held 15.00 2.00 30.00 0.00 Wrap
Text on our system. You may fax the 15.00 2.00 30.00 0.00 Wrap
Text form to the number above. 15.00 2.00 30.00 0.00 Wrap
Text Any information you may supply 17.00 2.00 30.00 0.00 Left
Text will not be made available to 17.00 2.00 30.00 0.00 Wrap
Text third parties according to the 17.00 2.00 30.00 0.00 Wrap
Text Privacy Act. 17.00 2.00 30.00 0.00 Wrap
Text Legal name: 20.50 2.00 30.00 0.00 Left
All I want is a column called 'nextAlign' that has the following data:
nextAlign
-Left
-Wrap
-Wrap
-Wrap
-Left
-Wrap
You didn't specify your DBMS, so this is ANSI SQL:
select type,
field,
align,
lead(align) over (order by starttop) as next_align,
starttop,
startleft
from the_table
use ROW_NUMBER() OVER(ORDER BY )
and outer join it to the same select this way: select_1_rownumber = select_2_rownumber+1
with temptable as
( select rownum
,type
,field
,starttop
,startleft
,length
,decimals
,alignment
from YOURTABLE )
select t.type
,t.field
,t.starttop
,t.startleft
,t.length
,t.decimals
,t.alignment
( select tt.alignment from temptable tt where tt.rownum = t.rownum+1 ) nextalign
from temptable t
Might work for you.