How to work past "At most one record can be returned by this subquery" - sql

I'm having trouble understanding this error through all the researching I have done. I have the following query
SELECT M.[PO Concatenate], Sum(M.SumofAward) AS TotalAward, (SELECT TOP 1 M1.[Material Group] FROM
[MGETCpreMG] AS M1 WHERE M1.[PO Concatenate]=M.[PO Concatenate] ORDER BY M1.SumofAward DESC) AS TopGroup
FROM MGETCpreMG AS M
GROUP BY M.[PO Concatenate];
For a brief instance it reviews the results I want, but then the "At most one record can be returned by this subquery" error comes and wipes all the data to #Name?
For context, [MGETCpreMG] is a query off a main table [MG ETC] that was used to consolidate Award for differing Material Groups on a PO transaction ([PO Concatenate])
SELECT [MG ETC].[PO Concatenate], Sum([MG ETC].Award) AS SumOfAward, [MG ETC].[Material Group]
FROM [MG ETC]
GROUP BY [MG ETC].[PO Concatenate], [MG ETC].[Material Group]
ORDER BY [MG ETC].[PO Concatenate];
I'm thinking it lies in my inability to understand how to utilize a subquery.

In the case in which the query can return more then one value? Simply add an additonal sort by.
So, a common sub query might be to get the last invoice. So you might have:
select ID, CompanyName,
(SELECT TOP 1 InvoiceDate from tblInvoice
where tblInvoice.CustomerID = tblCompany.ID
Order by InvoiceDate DESC)
As LastInvoiceDate
From tblCustomers
Now the above might work for some time, but then it will blow up since you might have two invoices for the same day!
So, all you have to do is add that extra order by clause - say on the PK of the child table like this:
Order by InvoiceDate DESC,ID DESC)
So top 1 will respect the "additional" order columns you add, and thus only ever return one row - even if there are multiple values that match the top 1 column.
I suppose in the above we could perhaps forget the invoiceDate and always take the top most last autonumber ID, but for a lot of queries, you can't always be sure - it might be we want the last most expensive invoice amount. And again, if the max value (top) was the same for two large invoice amounts, then again two rows could be return. So, simply add the extra ORDER BY clause with an 2nd column that further orders the data. And thus top 1 will only pull the first value. Your example of a top group is such an example. Just tack on the extra order by "ID" or whatever the auto number ID column is.

Related

How to compare dates from separate tables in Oracle

I am trying to compare two tables in my database. One of the tables (named ORDERED) has a field called OREDERDATE. Another table (named ORDERLINE) has a field called DATESUPPLIED.
I would like to run a SQL query to return the difference in days between the two dates.
I have tried a few things thus far (for example...
SELECT ordered.ordernumber, ordered.orderdate, orderline.datesupplied,
customer.customernumber,
LEAD (orderdate) OVER (partition by customernumber
ORDER BY customernumber) - datesupplied DIFF_DAYS
FROM ordered, orderline;
) ... but to no avail.
Please assist. My ERD follows
enter image description here
I think you want something like this:
select ol.ordernumber, ol.productcode,
trunc(ol.datesupplied) - trunc(o.orderdate) as days_difference
from orderline ol join ordered o on ol.ordernumber = o.ordernumber
;
This will only show the order number and the product code (therefore corresponding exactly to the rows in the second table) and the day difference between the supply date and the ordered date. trunc() is needed to truncate the time-of-day to midnight, since this is what you said you needed.
If you need additional columns from either table, add them to the select clause. If you must filter your results (for example, look only for specific orders, or orders before or after a given date, etc.), add a where clause. If you need to order the results in some way, add an order by clause.

Is it possible to SUM grouped rows into another column but only show the sum result on 1 of the rows?

I have a query table which includes data from several tables Joined with LEFT JOIN. So far everything works, with the resulting data being the individual items from Sales Orders. I can pull through the Total Sales Order Value into a column, but I only want this to be displayed on 1 of the rows for that Order OR into a separate row to represent the whole order.
My thoughts are that either I pull through the total but somehow only do this if it doesn't already appear for that order already OR I use SUM() but this seems to only work if used with GROUP BY and then I only get shown the totals and not the individual items.
I don't really know what to try as I don't even know if this is possible, and internet searches are not giving me any results that seem to help, they are all about Grouping.
SELECT
so."Sales Order#" as 'Sales Order Number',
soi."Sub Total" as 'Sub-Total'
FROM
"Sales Order Items" AS "soi"
LEFT JOIN
"Sales Orders" AS "so" ON so."Sales order ID" = soi."Sales order ID"
I would like to add a column 'TCV' which SUMS the 'Sub Total' values from all items that have the same Sales Order Number. But I only want this sub-total to show once per Order (not against every order item).
You can easily achieve this by using window functions in a case stmt:
SELECT
so.SalesOrderNumber as 'Sales Order Number',
CASE WHEN LAG(so.id) OVER (PARITION BY so.salesOrderNumber ORDER BY so.id) IS NULL --this row is first in group, order however you determine "First"
THEN soi.SubTotal
ELSE NULL
END AS subTotal
FROM
"Sales Order Items" AS soi LEFT JOIN
"Sales Orders" AS so ON so.salesOrderId = soi.salesOrderId
To create an additional row with the sum
Duplicate the query
Group the query by order
Sum the line value column
Use a Union to add this new query to the original.
Add an additional column to both, hard code the first to 'lines' and second to 'order'
You can add a total column
Duplicate the query
Group the query by order
Sum the line value column
Put the new query in the from clause of the original, give it an alias
Join the query by order number
Add the sum column of the new query to the select clause of the first query
Optionally, you can use use case statements to only show it for one line.

Join query in Access 2013

Currently have a single table with large amount of data in access, due to the size I couldn't easily work with it in Excel any more.
I'm partially there on a query to pull data from this table.
7 Column table
One column GL_GL_NUM contains a transaction number. ~ 75% of these numbers are pairs. I'm trying to pull the records (all columns information) for each unique transaction number in this column.
I have put together some code from googling that hypothetically should work but I think I'm missing something on the syntax or simply asking access to do what it cannot.
See below:
SELECT SOURCE_FUND, GLType, Contract, Status, Debit, Credit, GL_GL_NUM
FROM Suspense
JOIN (
SELECT TC_TXN_NUM TXN_NUM, COUNT(GL_GL_NUM) GL_NUM
FROM Suspense
GROUP BY TC_TXN_NUM HAVING COUNT(GL_GL_NUM) > 1 ) SUB ON GL_GL_NUM = GL_NUM
Hey Beth is this the suggested code? It says there is a syntax error in the FROM clause. Thanks.
SELECT * from SuspenseGL
JOIN (
SELECT TC_TXN_NUM, COUNT(GL_GL_NUM) GL_NUM
FROM Suspense
GROUP BY TC_TXN_NUM
HAVING COUNT(GL_GL_NUM) > 1
Do you want detailed results (all rows and columns) or aggregate results, with one row per tx number?
If you want an aggregate result, like the count of distinct transaction numbers, then you need to apply one or more aggregate functions to any other columns you include.
If you run
SELECT TC_TXN_NUM, COUNT(GL_GL_NUM) GL_NUM
FROM Suspense
GROUP BY TC_TXN_NUM
HAVING COUNT(GL_GL_NUM) > 1
you'll get one row for each distinct txn, but if you then join those results back with your original table, you'll have the same number of rows as if you didn't join them with distinct txns at all.
Is there a column you don't want included in your results? If not, then the only query you need to work with is
select * from suspense
Considering your column names, what you may want is:
SELECT SOURCE_FUND, GLType, Contract, Status, sum(Debit) as sum_debit,
sum(Credit) as sum_credit, count(*) as txCount
FROM Suspense
group by
SOURCE_FUND, GLType, Contract, Status
based on your comments, if you can't work with aggregate results, you need to work with them all:
Select * from suspense
What's not working? It doesn't matter if 75% of the txns are duplicates, you need to send out every column in every row.
OK, let's say
Select * from suspense
returns 8 rows, and
select GL_GL_NUM from suspense group by GL_GL_NUM
returns 5 rows, because 3 of them have duplicate GL_GL_NUMs and 2 of them don't.
How many rows do you want in your result set? if you want less than 8 rows back, you need to perform some sort of aggregate function on each column you want returned.
You could do something like the following:
SELECT S.* FROM
SUSPENSE AS S
INNER JOIN (SELECT DISTINCT GL_GL_NUM, MIN(ID) AS ID FROM SUSPENSE
GROUP BY GL_GL_NUM) AS S2
ON S.ID = S2.ID
AND S.GL_GL_NUM = S2.GL_GL_NUM
Which would return a single row for a unique gl_gl_num. However if the other rows have different data it will not be shown. You would have to either aggregate that data up using SUM(Credit), SUM(Debit) and then GROUP BY the gl_gl_num.
I have attached a SQL Fiddle to demonstrate my results and make this clearer.
http://sqlfiddle.com/#!3/8284f/2

Retrieving average cost

UPDATE: yes, the result is for a single record. I want to put this on a "computed by" field in firebird (PRODUCTS.PRODU_AVGCOST). It just takes the last 3 buyings for a product and calculates the average cost of it.
I have three tables, witch follows the revelant fields:
PRODUCTS
PRODU_ID PK,
PRODU_AVGCOST calculated
BUYINGS
BUYIN_ID PK
BUYING_ITENS
ITEN_ID PK,
ITEN_BUYING_ID FK
ITEN_COST numeric
I want to take the average cost of last three buyings from ITEN_COST field, using "select" for the PRODU_AVGCOST field of the PRODUCTS table
I tried as follows on table PRODUCTS, but this didn't work
select avg(iten_cost) from buying_itens b
where (select first 3 (iten_cost) from buying_itens where c.iten_id = produ_id)
order by b.iten_id desc
Assuming you want this for a single product (assuming ACTUAL_PRODU_ID is that value), you can do:
select avg(a.iten_cost)
from (
select first 3 iten_cost
from buying_itens
where produ_id = ACTUAL_PRODU_ID
order by iten_id desc
) a
As far as I can tell the datamodel in your question is not complete (or I fail to see how products and buying_itens are linked), so I have 'added' produ_id to buying_itens.
If you want to do this for all products in a single query, things get more complicated.

Is GROUP BY needed in the following correlated subquery?

Given scenario:
table fd
(cust_id, fd_id) primary-key and amount
table loan
(cust_id, l_id) primary-key and amount
I want to list all customers who have a fixed deposit with an amount less than the sum of all their loans.
Query:
SELECT cust_id
FROM fd
WHERE amount
<
(SELECT sum(amount)
FROM loan
WHERE fd.cust_id = loan.cust_id);
OR should we use
SELECT cust_id
FROM fd
WHERE amount
<
(SELECT sum(amount)
FROM loan
WHERE fd.cust_id = loan.cust_id group by cust_id);
A customer can have multiple loans but one FD is considered at a time.
GROUP BY can be omitted in this case, because there is only (one) aggregate function(s) in the SELECT list and all rows are guaranteed to belong to the same group of cust_id ( by the WHERE clause).
The aggregation will be over all rows with matching cust_id in both cases. So both queries are correct.
This would be a cleaner another way to implement the same thing:
SELECT fd.cust_id
FROM fd
JOIN loan USING (cust_id)
GROUP BY fd.cust_id, fd.amount
HAVING fd.amount < sum(loan.amount)
There is one difference: rows with identical (cust_id, amount) in fd only appear once in the result of my query, while they would appear multiple times in the original.
Either way, if there is no matching row with a non-null amount in table loan, you get no rows at all. I assume you are aware of that.
There are no need for GROUP BY since you filtered data by cust_id. In any case inner query will return the same result.
No, it isn't, because you calculate sum(amount) for customer with id = fd.cust_id, so for a single customer.
However, if somehow your subquery calculate sum for more than one customer, the group by would cause the subquery to generate more than one row and this will cause the condition(<) to fail, and thus, the query to fail.
A query with an aggregate like sum but without a group by will output one group. The aggregates will be computed over all matching rows.
A subquery in a condition clause is only allowed to return one row. If the subquery returned multiple rows, what would the following expression mean?
where 1 > (... subquery ...)
So the group by must be omitted; you would even get an error for your second query.
N.B. When you specify all, any, or some a subquery can return multiple rows:
where 1 > ALL (... subquery ...)
But it's easy to see why that doesn't make sense in your case; you'd compare one customer's data to that of another.