Oracle SQL Newbie needs help finding similar records - sql

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.

Related

How to calculate portfolio performance in Access

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.

Access query, grouped sum of 2 columns where either column contains values

Another team has an Access database that they use to track call logs. It's very basic, really just a table with a few lookups, and they enter data directly in the datasheet view. They've asked me to assist with writing a report to sum up their calls by week and reason and I'm a bit stumped on this problem because I'm not an Access guy by any stretch.
The database consists of two core tables, one holding the call log entries (Calls) and one holding the lookup list of call reasons (ReasonsLookup). Relevant table structures are:
Calls
-----
ID (autonumber, PK)
DateLogged (datetime)
Reason (int, FK to ReasonLookup.ID)
Reason 2 (int, FK to ReasonLookup.ID)
ReasonLookup
------------
ID (autonumber PK)
Reason (text)
What they want is a report that looks like this:
WeekNum Reason Total
------- ---------- -----
10 Eligibility Request 24
10 Extension Request 43
10 Information Question 97
11 Eligibility Request 35
11 Information Question 154
... ... etc ...
My problem is that there are TWO columns in the Calls table, because they wanted to log a primary and secondary reason for receiving the call, i.e. someone calls for reason A and while on the phone also requests something under reason B. Every call will have a primary reason column value (Calls.Reason not null) but not necessarily a secondary reason column value (Calls.[Reason 2] is often null).
What they want is, for each WeekNum, a single (distinct) entry for each possible Reason, and a Total of how many times that Reason was used in either the Calls.Reason or Calls.[Reason 2] column for that week. So in the example above for Eligibility Request, they want to see one entry for Eligibility Request for the week and count every record in Calls that for that week that has Calls.Reason = Eligibility Request OR Calls.[Reason 2] = Eligibility Request.
What is the best way to approach a query that will display as shown above? Ideally this is a straight query, no VBA required. They are non-technical so the simpler and easier to maintain the better if possible.
Thanks in advance, any help much appreciated.
The "normal" approach would be to use a union all query as a subquery to create a set of weeks and reasons, however Access doesn't support this, but what you can do that should work is to first define a query to make the union and then use that query as a source for the "main" query.
So the first query would be
SELECT datepart("ww",datelogged) as week, Reason from calls
UNION ALL
SELECT datepart("ww",datelogged), [Reason 2] from calls;
Save this as UnionQuery and make another query mainQuery:
SELECT uq.week, rl.reason, Count(*) AS Total
FROM UnionQuery AS uq
INNER JOIN reasonlookup AS rl ON uq.reason = rl.id
GROUP BY uq.week, rl.reason;
You can use a Union query to append individual Group By Aggregate queries for both Reason and Reason 2:
SELECT DatePart("ww", Calls.DateLogged) As WeekNum, ReasonLookup.Reason,
Sum(Calls.ID) As [Total]
FROM Calls
INNER JOIN Calls.Reason = ReasonLookup.ID
GROUP BY DatePart("ww", Calls.DateLogged) As WeekNum, ReasonLookup.Reason;
UNION
SELECT DatePart("ww", Calls.DateLogged) As WeekNum, ReasonLookup.Reason,
Sum(Calls.ID) As [Total]
FROM Calls
INNER JOIN Calls.[Reason 2] = ReasonLookup.ID
GROUP BY DatePart("ww", Calls.DateLogged) As WeekNum, ReasonLookup.Reason;
DatePart() outputs the specific date's week number in the calendar year. Also, UNION as opposed to UNION ALL prevents duplicate rows from appearing.

SQL : how to Case depending of the result of the 2 latest values of one column

I am discovering SQL as I have to build queries in my new company.I have understood the basic but here is where I am stuck, maybe you could help me figure this out :
I would like to mention a product as unprocurable if sellers rejected my orders twice. Tricky part I aggregate the furniture orders for all our local offices, therefore even though I sent my purchase order(s) to one unique seller (the one with the best offer at the moment) I might have multiple lines for each item (one per office)
See below table for purchase orders, see REF1 item should be set as unprocurable as both on 21 and 31 december my orders have been rejected (no matter the seller)
http://i.stack.imgur.com/r3W3E.jpg
So to put it in logic I would like to have something like this:
For each items with 2 latest purchase orders that were both made at different dates and rejected(0 value in the table) THEN attach a note to it saying "unprocurable" else put as procurable.
IF it was only 1 value I think I could go with
Select
item
, MAX(date)
, case
when confirmed_units = 0
then 'Unprocurable'
else 'procurable'
end
From
purchase_table
Where
date between TO_DATE('01/01/2013', 'MM/DD/YYYY') AND TO_DATE('{RUN_DATE_YYYY/MM/DD}', 'YYYY/MM/DD')
But now I need to check the two latest purchase orders and that are not from the same day.
I am a bit lost, could you give a hand please?
Thanks !
Your question is a little unclear... have you tried using something along the lines of:
SELECT TOP 2 etc, etc... order by [column]

How do I set ORDER BY in SQL query to a value depending by the SQL query itself?

Imagine an auction (ebay auction, for example). You create an auction, set the start bidding value, let's say, 5 dollars. This gets stored as a minimal bid value to the auctions table.At this point, the current bid value of this auction is 5 dollars.
Now, if someone bids to your auction, let's say, 10 dollars, this gets stored to the bids table.At this point, the current bid value of this auction is 10 dollars.
Now let's imagine you want to retrieve 5 cheapest auctions. You will write a query like this:
SELECT
`auction_id`,
`auction_startPrice`,
MAX(bids.bid_price) as `bid_price`
FROM
`auctions`
LEFT JOIN `bids` ON `auctions`.`auction_id`=`bids`.`bid_belongs_to_auction`
GROUP BY `auction_id`
LIMIT 5
Pretty simple, and it works! But now you need to add an ORDER BY clause to the query. The problem is, however, that we want to ORDER BY either by auctions.auction_startPrice or by bid_price, depending on whichever of this is higher, as explained in the first paragraphs.
Can this be understood? I know how to do this using 2 queries, but I am hoping it can be done with 1 query.
Thanks!
EDIT: Just a further explanation to help you imagine the problem. If I set ORDER BY auction_startPrice ASC, then I will get 5 auctions with their lowest initial bid price, but what if there are already bids placed on those auctions? Then their current lowest price is equal to those bids, NOT to the start price, therefore my query is wrong.
SELECT
`auction_id`,
`auction_startPrice`,
`bid_price`
FROM
(
SELECT
`auction_id`,
`auction_startPrice`,
MAX(bids.bid_price) as `bid_price`,
IF(MAX(bids.bid_price)>`auction_startPrice`,
MAX(bids.bid_price),
`auction_startPrice`) higherPrice
FROM
`auctions`
LEFT JOIN `bids` ON `auctions`.`auction_id`=`bids`.`bid_belongs_to_auction`
GROUP BY `auction_id`
) X
order by higherPrice desc
LIMIT 5;
Note:
In the inner query, an extra column is created, named 'higherPrice'
The IF function compares the MAX(bid_price) column against the startprice, and only if the Max-bid is not null (implicitly required in comparison) and greater than start price, then the Max-bid becomes the value in the higherPrice column. Otherwise, it will contain the start price.
The outer query merely makes use of the columns from the inner query, ordering by the higherPrice
I'm not sure which database you're using but look at this example:
http://www.extremeexperts.com/sql/articles/CASEinORDER.aspx
SELECT
`auction_id`,
`auction_startPrice`,
MAX(bids.bid_price) as `bid_price`
FROM
`auctions`
LEFT JOIN `bids` ON `auctions`.`auction_id`=`bids`.`bid_belongs_to_auction`
GROUP BY `auction_id`
ORDER BY CASE WHEN `auction_startPrice` > isnull(MAX(bids.bid_price),0) then `auction_startPrice` else MAX(bids.bid_price) end
LIMIT 5

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?