How to calculate portfolio performance in Access - sql

I've found this question particularly hard to google as the search terms come up with matching results, but not what I'm looking for...
I am trying to calculate the performance of a stock over a given time period using MS Access.
Example Calculation:
01.01.2016 Price: 100.00
25.02.2016 Price: 110.00
Pseudo Code:
Performance = Ending Price/Starting Price - 1
As simple as this would be to calculate in Excel, I can't seem to get it done in MS Access. My attempt thus far already fails on the simple task to get the end date. The Failure message is:
"You tried to execute a query that does not include the specified
expression'MoPo_BM_ID'as part of an aggregate function"
UPDATE:
I found a website that included some code that creates the desired result. The example works fine, but I tried adapting it to my own situation but am getting the error message "At most one record can be returned by this subquery".
SELECT x.Mandat_ID, x.BeginPrice, x.EndPrice, ([EndPrice]-[BeginPrice])/[BeginPrice] AS RETURN
FROM (SELECT Mandat_ID,
(SELECT Value AS BeginDate
FROM tbl_AMSDB_IndexMFP AS P
WHERE (Mandat_ID = Mandat_ID)
AND (Date =
(SELECT MIN(Date) AS Expr1
FROM tbl_AMSDB_IndexMFP AS D
WHERE (Mandat_ID = Mandat_ID)))) AS BeginPrice,
(SELECT Value AS BeginDate
FROM tbl_AMSDB_IndexMFP AS P
WHERE (Mandat_ID = Mandat_ID)
AND (Date =
(SELECT MAX(Date) AS Expr1
FROM tbl_AMSDB_IndexMFP AS D
WHERE (Mandat_ID = Mandat_ID)))) AS EndPrice
FROM tbl_AMSDB_IndexMFP
GROUP BY Mandat_ID) AS x
WHERE Mandat_ID=6028;
How can I get it to show me one record?

To get the max date to work, you need to group by the other fields in the select. So your query becomes
SELECT
tbl_MoPo_BM.MoPo_BM_ID,
tbl_AMSDB_IndexMFP.Mandat_ID,tbl_MoPo_BM.Launch_Date,
Max([tbl_AMSDB_IndexMFP]![Date]) AS End_Date
FROM
tbl_AMSDB_IndexMFP
INNER JOIN tbl_MoPo_BM ON tbl_AMSDB_IndexMFP.Mandat_ID =
tbl_MoPo_BM.AMSDB_MoPo_Code
GROUP BY
tbl_MoPo_BM.MoPo_BM_ID,
tbl_AMSDB_IndexMFP.Mandat_ID,
tbl_MoPo_BM.Launch_Date,
WHERE (((tbl_MoPo_BM.MoPo_BM_ID)=107))
Without knowing what your value/price field(s) are, whether the Launch_Date is the start date and whether duplicate entries are in your indexMFP table I can't write the rest to give you the answer to your Pseudo Performance calculation.

Related

why does this query not give me only the specified accounts?

Oracle SQL Developer
I expect to see:
In the subquery, I have that the rownumber be less than 2. When I run this query separately, it gives me 2 accounts. However, when I'm running the entire query, the list of account numbers just goes on! what's happening here?
SELECT m.acctno, i.intervalstartdate, d.name, i.intervalvalue
FROM endpoints E
JOIN meters m on m.acctid = e.acctid
LEFT JOIN intervaldata I ON I.acctid = M.acctid
LEFT JOIN endpointmodels EM ON EM.endpointmodelid=E.hwmodelid
LEFT JOIN datadefinitions D ON D.datadefinitionid = I.datadefinitionid
WHERE 1=1
AND E.statuscodeid = 8
AND m.FORM = 2
and exists
(
SELECT m2.acctno
from acct m2
where m2.acctno is not null
--and m2.acctno=m2.acctno
and rownum <= 2
)
AND D.datadefinitionid =7077
AND I.intervalstartdate BETWEEN '24-SEP-2017 00:00' and '25-SEP-2017 00:00'
--TRUNC(sysdate - 1) + interval '1' hour AND TRUNC(sysdate - 1) + interval
'24' hour
ORDER BY M.acctno, I.intervalstartdate, I.datadefinitionid
This query is supposed to give me 97 rows for each account. The data i'm reading, the interval values, are the data we report for each customer in 96 intervals. so Im expecting for 2 accounts for example, to get 194 rows. i want to test for 2 accounts now, but then i want to run for 50,000. so with 2, it's not even working. Just giving me millions of rows for two accounts. Basicaly, i think my row num line of code is being ignored. I can't use an in clause because i cant pass 50,000 accounts into there. so I used the exist operator.
Let me know!
I think the error is in trying to use and exists (...) clause. The exists predicate returns true if the subquery returns any rows at all. So, in your case, the result of exists will always be true, unless the table is empty. This means it has no effect whatsoever on the outer query. You need to use something like
inner join (SELECT m2.acctno
from acct m2
where m2.acctno is not null
--and m2.acctno=m2.acctno
and rownum <= 2) sub1
on sub1.acctno = m.acctno
to get what you want instead of and exists (...).
One obvious mistake is the date condition, where you require a date to be between two STRINGS. If you keep dates in string format, you will run into thousands of problems and bugs and you won't be able to fix them.
Do you understand that '25-APR-2008 00:00:00' is between '24-SEP-2017 00:00:00' and '25-SEP-2017 00:00:00', if you compare them alphabetically (as strings)?
The solution is to make sure the date column is in DATE (or TIMESTAMP) data type, and then to compare to dates, not to strings.
As an aside - this will not cause any errors, but it is still bad code - in the EXISTS condition you have a condition for ROWNUM <= 2. What do you think that will do? The subquery either returns at least one row (the first one will automatically have ROWNUM = 1) or it doesn't. The condition on ROWNUM in that subquery in the EXISTS condition is just garbage.

Writing a query to include certain values but exclude others when looking for a latest time period

I am trying to write a query that looks for a people that have a certain code with the latest period (year) but not if they have another code with that latest period(year). I'll be explicit just so my example makes sense.
I want people who have the code A1,A2,A3,A4,A5 but not AG,AP,AQ. There are people who have an A1 code for a period (like 2014) and an AG code for a the same period. I'd like to exclude them. Not everyone has a code so the field value could be NULL.
Is there a way to express this in a different way (i.e. less characters) than the way I did?
SELECT
people.firstName
FROM
people
WHERE EXISTS (
SELECT *
FROM codes
WHERE
codes.people_id = people.id
AND period = (SELECT MAX(period) FROM codes codes2 WHERE codes2.people_id = codes.people_id)
AND code LIKE 'A[1-5]'
)
AND NOT EXISTS (
SELECT *
FROM codes
WHERE
codes.people_id = people.id
AND period = (
SELECT MAX(period)
FROM codes codes2
WHERE codes2.people_id = codes.people_id
)
AND code LIKE 'A[GPQ]'
)
Schema is as follows:
People
id (PK)
firstName
Codes
people_id (FK) many to one relation with People table
code (e.g. "A1", "A2", "AG")
period (e.g. "2013", "2014")
There are so many ways you could do that, I'm not an SQL expert but I can't see your query being too bad, if you want to try and reduce the number of sub-queries you could consider using the GROUP BY clause along with a SUM Aggregate function in a HAVING clause.
I started updating your code as follows:
SELECT
people.firstName
FROM
people
LEFT JOIN codes AS a15 ON a15.people_id = people.id AND a15.code LIKE 'A[1-5]'
LEFT JOIN codes AS agpq ON agpq.people_id = people.id AND agpq.code LIKE 'A[GPQ]'
GROUP BY
people.firstName
HAVING
SUM(CASE WHEN a15.code IS NULL THEN 0 ELSE 1 END) > 0
AND SUM(CASE WHEN agpq.code IS NULL THEN 0 ELSE 1 END) = 0
This however doesn't take into account anything to do with period specific requirements described. You could add the period to the GROUP BY clause or add it to a WHERE or one of the JOIN constraints but I'm not quite sure from your description exactly what you're after (I don't believe this is through any fault of your own, I just can't personally align the code provided to the description).
I would also like to point out that the SUM functions above will not give an accurate count of the number of matching codes. This is because if both A[GPQ] and A[1_5] return at least one row, the number returned by each constraint will be multiplied by the number returned for the other, it can however be used to determine if there are "any" returned items as if the criteria is matched it will have a SUM(...) > 0
I'm sure a more experienced SQL Developer / DBA will be able to poke many holes in my proposed query but it might give them or someone else something to work from and hopefully gives you ideas for alternatives to using sub-queries.

Oracle SQL Newbie needs help finding similar records

Please forgive my naivety, I’m an Oracle SQL newbie using Toad. I have a table with sales records, call it Sales. It has records of customers (by CustID) the date of a sale (SaleDate) and the item sold (by ItemID). It’s an Mview actually of other tables with final sales status in it.
I am trying to construct a query to return CustID, SaleDate, and ItemID if there is a sale on the same day for that customer for both ItemID=A and ItemID=B if between SaleDate 7/1/2013 and 7/31/2013. If this condition exists I want both records returned with the CustID, SaleDate and ItemID. I assume the two records would be on separate rows.
I’ve been researching IN, EXISTS, and sub queries but have yet to strike upon the right approach. The table has about 7 million records on it so I need something fairly efficient. Can someone point me in the right direction to achieve this? I’m learning, but I need to learn faster :)
GOT IT WOKING!
2/24/2014: Hey, I got it working and it returns the results on thesame row. One caveat to this. In my orginal example I was looking for dates when both 5P311 and 6R641 existed. In actuality I wanted all the days where 5P311 and any of the values from the RES group exists - of which 6R641 is a member. The code below achieves the results as I need them:
SELECT ItemA.CLM_SSN,
ItemA.CLM_SERV_STRT Service_Date,
ItemA.CLM_COST_CTR_NBR,
ItemA.CLM_RECV_AMT,
ItemB.CLM_COST_CTR_NBR RES_Cost_Center,
ItemB.CLM_RECV_AMT,
GroupCode,
Service
FROM DDIS.PTS_MV_CLM_STAT ItemA,
DDIS.PTS_MV_CLM_STAT ItemB,
DDIS.CST_SERV
WHERE TRUNC(ItemA.CLM_SERV_STRT) between to_date ('01-07-2013','dd-mm-yyyy') and to_date('31- 07-2013','dd-mm-yyyy')
and TRUNC(ItemA.CLM_SERV_STRT) = TRUNC(ItemB.CLM_SERV_STRT)
and TRIM(ItemA.CLM_COST_CTR_NBR) = '5P311'
and ITEMB.FK_SERV = CST_SERV.PKSERVICE
and CST_SERV.GroupCode = 'RES'
and Itema.CLM_SSN = ItemB.CLM_SSN
and ItemA.CLM_RECV_AMT <> 0
and ItemB.CLM_RECV_AMT <> 0
ORDER BY ItemA.CLM_SSN, ItemA.CLM_SERV_STRT
Try this, replace 'A' and 'B' values of course
SELECT CustID, SaleDate, ItemID
FROM Mview AS mv
WHERE EXISTS(SELECT 1 FROM Mview AS itemA WHERE itemA.ItemID = 'A'
AND TRUNC(itemA.SaleDate) = TRUN(mv.SaleDate) )
AND EXISTS(SELECT 1 FROM Mview AS itemB WHERE itemB.ItemID = 'B'
AND TRUNC(itemB.SaleDate) = TRUNC(mv.SaleDate) )
AND mv.SaleDate BETWEEN TO_DATE ('2003/01/07', 'yyyy/mm/dd')
AND TO_DATE ('2003/01/31', 'yyyy/mm/dd');
The exists combined ensures you that there is a sell that day that had those 2 items, the TRUNC in the date is to get rid of the hours and minutes of the date.
The between lets you seek the current range of dates, you have to convert it to date, since you are passing a string.
Edit:
ItemA is a alias for the table Mview inside the exists oracle: can you assign an alias to the from clause? sql understand alias without the AS, but you can put it if it makes it easier for you to read.
In the full example you posted, you are not using any alias for DDIS.PTS_MV_CLM_STAT, so, the database motor doesnt distict wich table you are refering and that's why you dont get the values you want.

Update table with combined field from other tables

I have a table with actions that are being due in the future. I have a second table that holds all the cases, including the due date of the case. And I have a third table that holds numbers.
The problems is as follows. Our system automatically populates our table with future actions. For some clients however, we need to change these dates. I wanted to create an update query for this, and have this run through our scheduler. However, I am kind of stuck at the moment.
What I have on code so far is this:
UPDATE proxima_gestion p
SET fecha = (SELECT To_char(d.f_ult_vencim + c.hrem01, 'yyyyMMdd')
FROM deuda d,
c4u_activity_dates c,
proxima_gestion p
WHERE d.codigo_cliente = c.codigo_cliente
AND p.n_expediente = d.n_expediente
AND d.saldo > 1000
AND p.tipo_gestion_id = 914
AND p.codigo_oficina = 33
AND d.f_ult_vencim > sysdate)
WHERE EXISTS (SELECT *
FROM proxima_gestion p,
deuda d
WHERE p.n_expediente = d.n_expediente
AND d.saldo > 1000
AND p.tipo_gestion_id = 914
AND p.codigo_oficina = 33
AND d.f_ult_vencim > sysdate)
The field fecha is the current action date. Unfortunately, this is saved as a char instead of date. That is why I need to convert the date back to a char. F_ult_vencim is the due date, and hrem01 is the number of days the actions should be placed away from the due date. (for example, this could be 10, making the new date 10 days after the due date)
Apart from that, there are a few more criteria when we need to change the date (certain creditors, certain departments, only for future cases and starting from a certain amount, only for a certain action type.)
However, when I try and run this query, I get the error message
ORA-01427: single-row subquery returns more than one row
If I run both subqueries seperately, I get 2 results from both. What I am trying to accomplish, is that it connects these 2 queries, and updates the field to the new value. This value will be different for every case, as every due date will be different.
Is this even possible? And if so, how?
You're getting the error because the first SELECT is returning more than one row for each row in the table being updated.
The first thing I see is that the alias for the table in UPDATE is the same as the alias in both SELECTs (p). So all of the references to p in the subqueries are referencing proxima_gestion in the subquery rather than the outer query. That is, the subquery is not dependent on the outer query, which is required for an UPDATE.
Try removing "proxima_gestion p" from FROM in both subqueries. The references to p, then, will be to the outer UPDATE query.

SQL Output Question

Edited
I am running into an error and I know what is happening but I can't see what is causing it. Below is the sql code I am using. Basically I am getting the general results I want, however I am not accurately giving the query the correct 'where' clause.
If this is of any assistance. The count is coming out as this:
Total Tier
1 High
2 Low
There are 4 records in the Enrollment table. 3 are active, and 1 is not. Only 2 of the records should be displayed. 1 for High, and 1 for low. The second Low record that is in the total was flagged as 'inactive' on 12/30/2010 and reflagged again on 1/12/2011 so it should not be in the results. I changed the initial '<=' to '=' and the results stayed the same.
I need to exclude any record from Enrollments_Status_Change that where the "active_status" was changed to 0 before the date.
SELECT COUNT(dbo.Enrollments.Customer_ID) AS Total,
dbo.Phone_Tier.Tier
FROM dbo.Phone_Tier as p
JOIN dbo.Enrollments as eON p.Phone_Model = e.Phone_Model
WHERE (e.Customer_ID NOT IN
(Select Customer_ID
From dbo.Enrollment_Status_Change as Status
Where (Change_Date >'12/31/2010')))
GROUP BY dbo.Phone_Tier.Tier
Thanks for any assistance and I apologize for any confusion. This is my first time here and i'm trying to correct my etiquette on the fly.
If you don't want any of the fields from that table dbo.Enrollment_Status_Change, and you don't seem to use it in any way — why even include it in the JOINs? Just leave it out.
Plus: start using table aliases. This is very hard to read if you use the full table name in each JOIN condition and WHERE clause.
Your code should be:
SELECT
COUNT(e.Customer_ID) AS Total, p.Tier
FROM
dbo.Phone_Tier p
INNER JOIN
dbo.Enrollments e ON p.Phone_Model = e.Phone_Model
WHERE
e.Active_Status = 1
AND EXISTS (SELECT DISTINCT Customer_ID
FROM dbo.Enrollment_Status_Change AS Status
WHERE (Change_Date <= '12/31/2010'))
GROUP BY
p.Tier
Also: most likely, your EXISTS check is wrong — since you didn't post your table structures, I can only guess — but my guess would be:
AND EXISTS (SELECT * FROM dbo.Enrollment_Status_Change
WHERE Change_Date <= '12/31/2010' AND CustomerID = e.CustomerID)
Check for existence of any entries in dbo.Enrollment_Status_Change for the customer defined by e.CustomerID, with a Change_Date before that cut-off date. Right?
Assuming you want to:
exclude all customers whose latest enrollment_status_change record was since the start of 2011
but
include all customers whose latest enrollment_status_change record was earlier than the end of 2010 (why else would you have put that EXISTS clause in?)
Then this should do it:
SELECT COUNT(e.Customer_ID) AS Total,
p.Tier
FROM dbo.Phone_Tier p
JOIN dbo.Enrollments e ON p.Phone_Model = e.Phone_Model
WHERE dbo.Enrollments.Active_Status = 1
AND e.Customer_ID NOT IN (
SELECT Customer_ID
FROM dbo.Enrollment_Status_Change status
WHERE (Change_Date >= '2011-01-01')
)
GROUP BY p.Tier
Basically, the problem with your code is that joining a one-to-many table will always increase the row count. If you wanted to exclude all the records that had a matching row in the other table this would be fine -- you could just use a LEFT JOIN and then set a WHERE clause like Customer_ID IS NULL.
But because you want to exclude a subset of the enrollment_status_change table, you must use a subquery.
Your intention is not clear from the example given, but if you wanted to exclude anyone who's enrollment_status_change as before 2011, but include those who's status change was since 2011, you'd just swap the date comparator for <.
Is this any help?