Include subselect only if there is one result using tsql - sql-server

We have an invoice, a invoice detail and a order table and the tables are linked by the invoice detail rows because the invoice details are grouped by delivery date so a invoice often covers multiple order numbers.
Now I would like to build a view that would display the order number if there is only one order involved in the invoice by using a subselect of some kind.
I came up with this one but it still generates an error reporting that the subquery return more than one result
SELECT Invoice.Id, Invoice.TotalAmount,
(SELECT DISTINCT OrderId FROM InvoiceDetail
WHERE InvoiceDetail.InvoiceId = Invoice.Id
GROUP BY OrderId HAVING COUNT(DISTICT OrderId) = 1) AS OrderId
FROM Invoice
Any ideas to get this to work?

How about:
SELECT
Invoice.Id,
Invoice.TotalAmount,
OneOrder.OrderId
FROM
Invoice
LEFT JOIN (
SELECT InvoiceId, MIN(OrderId) OrderId
FROM InvoiceDetail
GROUP BY InvoiceId
HAVING COUNT(DISTINCT OrderId) = 1
) OneOrder ON OneOrder.InvoiceId = Invoice.Id

Tested correct:
SELECT Id, TotalAmount, OrderInfo.OrderId
FROM Invoice
JOIN
(
SELECT InvoiceId, OrderId
FROM InvoiceDetail
JOIN Invoice
ON InvoiceDetail.InvoiceId = Invoice.Id
GROUP BY InvoiceId, OrderId
HAVING COUNT(OrderId)=1
) AS OrderInfo
ON Invoice.Id=OrderInfo.InvoiceId
Notice lack of DISTINCT in HAVING clause, which is incorrect (it would cause multiple order ids to count as one, breaking the expected behavior)

Change
GROUP BY OrderId
to
GROUP BY InvoiceDetail.InvoiceId

Your problem may just be the typo in the HAVING clause. See DISTICT.

Related

SQL Project using a where clause

So this is what I am working with new to sql and still learning been stuck on this for a few days now. Any advice would be appreciated I attached the image of the goal I'm trying to achieve
OrderItem And Product Table
Order And OrderItem Table(https://i.stack.imgur.com/pdbMT.png)
Scenario: Our boss would like to see the OrderNumber, OrderDate, Product Name, UnitPrice and Quantity for products that have TotalAmounts larger than the average
Create a query with a subquery in the WHERE clause. OrderNumber, OrderDate and TotalAmount come from the Order table. ProductName comes from the Product table. UnitPrice and Quantity come from the OrderItem table.
This is the code I came up with but it causes product name to run endlessly and displays wrong info.
USE TestCorp;
SELECT DISTINCT OrderNumber,
OrderDate,
ProductName,
i.UnitPrice,
Quantity,
TotalAmount
FROM [Order], Product
JOIN OrderItem i ON Product.UnitPrice = i.UnitPrice
WHERE TotalAmount < ( SELECT AVG(TotalAmount)
FROM [Order]
)
ORDER BY TotalAmount DESC;
Best guess assuming joins and fields not provided.
SELECT O.OrderNumber, O.orderDate, P.ProductName, OI.UnitPrice, OI.Quantity, O.TotalAmount
FROM [Order] O
INNER JOIN OrderItem OI
on O.ID = OI.orderID
INNER JOIN Product P
on P.ID= OI.ProductID
CROSS JOIN (SELECT avg(TotalAmount) AvgTotalAmount FROM [Order]) z
WHERE O.TotalAmount > z.AvgTotalAmount
Notes:
You're mixing join notations don't use , and inner join together that's mixing something called ANSI Standards.
I'm not sure why you have a cross join to product to begin with
You don't specify how to join Order to order item.
It seems very odd to be joining on Price.... join on order ID or productID maybe?
you could cross join to an "Average" result so it's available on every record. (I aliased this inline view "Z" in my attempt)
so what the above does is include all Orders. and for each order, an order item must be associated for it to be included. And then for each order item, a productid must be included and related to a record in product. If for some reason an order item record doens't have a related entry in product table, it gets excluded.
I use a cross join to get the average as it's executed 1 time and applied/joined to every record.
If we use the query in the where clause it's executed one time for EVERY record (unless the DB Engine optimizer figures it out and generates a better plan)
I Assume
Order.ID relates to OrderItem.OrderID
OrderItem.productID relates to Product.ID
Order.TotalAmount is what we are wanting to "Average" and compare against
Every Order has an Order Item entry
Every Order Item entry has a related product.

SELECT statement with sub-query

Instructions:
Business case: The accounting department would like a reporting of the top ten vendors with their last invoice date and average invoice amount.
Write a SELECT statement that returns three columns:
VendorName (from the Vendors table)
LatestInv (summary function that returns the last entry from InvoiceDate)
AverageInv: (summary function that returns the average from InvoiceTotal)
Hint: you will need to join two tables before joining to the derived table (the subquery)
Subquery portion: SELECT statement that returns the top ten VendorID and AverageInv (same name and function as described in the outer query). Group the results by the appropriate column and sort the results by AverageInv from largest to smallest. Correlate the subquery as BestVendors and join it to the correct table (where both share a key field).
Group the outer query by the appropriate column and sort the results by LatestInv
most recent to oldest
My code
SELECT VendorName, MAX(InvoiceDate) AS LatestInv, AVG(InvoiceTotal) AS AverageInv
FROM Vendors v JOIN
(SELECT TOP 10 VendorID, AVG(InvoiceTotal) AS AverageInv
FROM Invoices
GROUP BY VendorID
ORDER BY AverageInv DESC) AS BestVendors
ON v.VendorID = BestVendors.VendorID
GROUP BY VendorName
ORDER BY LatestInv
MAX(InvoiceDate) has a red line under it as well as AVG(InvoiceTotal) because they are from the Invoices table. Not the Vendors. However if I use FROM Invoices in the outer query then VendorName won't be recognized? How do I fix this and get the result set that this question is looking for?
Also these pics show some sample data from the Invoices and Vendors Table
Try this:
SELECT VendorName, BestVendors.LatestInv, BestVendors.AverageInv
FROM Vendors v
INNER JOIN
(
SELECT TOP 10 VendorID
,AVG(InvoiceTotal) AS AverageInv
,MAX(InvoiceDate) AS LatestInv
FROM Invoices
GROUP BY VendorID
ORDER BY AverageInv DESC
) AS BestVendors
ON v.VendorID = BestVendors.VendorID
ORDER BY LatestInv DESC

Create a temporary table showing the most eventful country for each year in SQL Server

I have an exercise in SQL Server: I have two tables Country and Events.
The Events table holds the event details including the city where an event happens. The table Events has a foreign key CountryID (CountryID is the primary key in table Country).
I need to create a temporary table showing the most eventful country for each year.
Any help would be appreciated
Thanks
You weren't far off with your attempt, but you need to use a CTE to aggregate your data first. I've assumed that the final order of your data is important, so I used a second CTE, rather than a TOP 1 WITH TIES tio get the final result:
WITH CTE AS(
SELECT YEAR(e.EventDate) AS YearOfEvent,
c.CountryName,
COUNT(e.CountryID) AS NumberOfEvents
FROM [dbo].[tblEvent] AS e
INNER JOIN tblCountry AS c ON e.CountryID = c.CountryID
GROUP BY e.CountryId,
c.CountryName,
YEAR(e.EventDate)),
RNs AS(
SELECT YearOfEvent,
CountryName,
NumberOfEvents,
ROW_NUMBER() OVER (PARTITION BY YearOfEvent ORDER BY CTE.NumberOfEvents DESC) AS RN
FROM CTE)
SELECT YearOfEvent,
CountryName,
NumberOfEvents
FROM RNs
WHERE RN = 1
ORDER BY RNs.YearOfEvent ASC;

Select all Item From Table Where Order has any Items between dates

I have an Orders table and an OrderItem table. I would like to select all OrderItems that have been shipped between 2 dates, and select the additional OrderItem of a certain type that was shipped outside of the 2 dates if it's part of an Order that has OrderItems shipped between the 2 dates.
This seemed really easy when I first thought of it, but I'm having a hard time putting it into a SQL statement. I'm using SQL Server.
EDIT:
Yes, I am familiar with the between keyword. What I have is an Order, Say Order #10001. It has 2 items, a product that is shipped on 01/20/2015 and a warranty that is marked as shipped on 02/04/2015. So when I run my query:
SELECT *
FROM OrderItems
WHERE ShipDate BETWEEN '01/01/2015' AND '01/31/2015'
I only get the 1 product, I want to get the warranty that is on the Order as well.
Hope that clarifies my question.
You can do this like this:
SELECT *
FROM OrderItems
WHERE OrderID IN(
SELECT DISTINCT OrderID
FROM OrderItems
WHERE ShipDate BETWEEN '01/01/2015' AND '01/31/2015'
)
Or:
SELECT *
FROM OrderItems oi1
JOIN (
SELECT DISTINCT OrderID
FROM OrderItems
WHERE ShipDate BETWEEN '01/01/2015' AND '01/31/2015'
) oi2 ON oi1.OrderID = oi2.OrderID
Are you familiar with BETWEEN keyword?
SELECT ...
WHERE col BETWEEN AND
If you add more information, such as sample data to your question, I can elaborate on the answer.

Select a random database row based on another query

For internal control we would like to select a single random invoice for each of multiple invoice types and regions.
Here's the SQL to get a set of distinct Invoice Types and Regions
select InvoiceType,RegionID
from Invoices
group by InvoiceType, RegionID
For each row this returns I need to fetch a random row with that InvoiceType and RegionID. This is how I'm fetching random rows:
SELECT top 1
CustomerID
,InvoiceNum
,Name
FROM Invoices
JOIN Customers on Customers.CustomerID=Invoices.CustomerID
where InvoiceType=X and RegionID=Y
ORDER BY NEWID
But I don't know how to run this select statement foreach() row the first statement returns. I could do it programmatically but I would prefer an option using only a stored procedure as this query isn't supposed to need a program.
WITH cteInvoices AS (
SELECT CustomerID, InvoiceNum, Name,
ROW_NUMBER() OVER(PARTITION BY InvoiceType, RegionID ORDER BY NEWID()) AS RowNum
FROM Invoices
)
SELECT c.CustomerID, c.InvoiceNum, c.Name
FROM cteInvoices c
WHERE c.RowNum = 1;

Resources