Finding the total amount for my query - sql

I have a query that allows me to retrieve customer orders.
Select ID, OrderID, Item, Price from customerOrders
Where Order = 1
is there a way of totalling up the final amount to pay and have it visible below, but still be able to view all details needed in the sql query, for example
ID | OrderID | Item | Price
1 | 1 | Book | 9.99
2 | 1 | DVD | 12.99
total = 22.98
Many thanks in advance

Two steps:
1, To generate the SUM, you need to add it to the result set using UNION ALL. UNION adds a DISTINCT operation which isn't required, even if in your case it won't change the result.
Select ID, OrderID, Item, Price from customerOrders
Where [Order] = 1
UNION ALL
Select NULL, NULL, NULL, SUM(Price) from customerOrders
Where [Order] = 1
ORDER BY CASE WHEN ID IS NULL THEN 2 ELSE 1 END, ID;
2, To sort it in the order you want (sum at the bottom), you can use the NULL ID as an identifier.

You can try COMPUTE
Select ID, OrderID, Item, Price from customerOrders
Where Order = 1
COMPUTE SUM(Price)

Technically, the thing you want is called a roll-up row. SQL Server has a special function for such cases, it's called ROLLUP() and used inside a GROUP BY clause. In your particular situation, the query would basically look like this:
SELECT ID, OrderID, Item, SUM(Price) AS Price
FROM customerOrders
WHERE OrderID = 1
GROUP BY ROLLUP((ID, OrderID, Item))
;
You can see a live demonstration of this query at SQL Fiddle. More information about ROLLUP(), as well as other GROUP BY functions, can be found at MSDN.

This is what you need:
Select ID, OrderID, Item, Price from customerOrders
Where Order = 1
Union
Select NULL, NULL, NULL, (
Select Sum(Price) from customerOrders
Where Order = 1
Group By Order
)
UPDATE
Incase the result was empty, the total should be zero, not empty. Use the updated code.
Cheers

Related

SQL querying a customer ID who ordered both product A and B

Having a bit of trouble when trying to figure out how to return a query of a customer who ordered both A and B
What I'm looking for is all customers who order both product A and product B
SELECT CustomerID
FROM table
WHERE product in ('a','b')
GROUP BY customerid
HAVING COUNT(distinct product) = 2
I don't normally post code only answers but there isn't a lot that words can add to this- the query predominantly explains itself
You can also
HAVING max(product) <> min(product)
It may be worth pointing out that in queries, the WHERE is performed, filtering to just products A and B. Then the GROUP BY is performed, grouping customer and counting the distinct number of products (or getting the min and max). Then the HAVING is performed, filtering to just those with 2 distinct products (or getting only those where MIN i.e. A, is different to MAX i.e. B)
If you'v never encountered HAVING, it is logically equivalent to:
SELECT CustomerID
FROM(
SELECT CustomerID, COUNT(distinct product) as count_distinct_product
FROM table
WHERE product in ('a','b')
GROUP BY customerid
)z
WHERE
z.count_distinct_product = 2
In a HAVING clause you can only refer to columns that are mentioned in the group by. You can also refer to aggregate operations (such as count/min/max) on other columns not mentioned in the group by
I have never worked with SQLLite, but since it's specs say it is a Relational Database, it should allow the following query.
select CustomerID
from table t
where exists (
select *
from table
where CustomerID = t.CustomerID
and Product = 'A'
)
and exists (
select *
from table
where CustomerID = t.CustomerID
and Product = 'B'
)
I'd use a correlated sub-query with a HAVING clause to scoop in both products in a single WHERE clause.
SELECT
t.Customer
FROM
#t AS t
WHERE
EXISTS
(
SELECT
1
FROM
#t AS s
WHERE
t.Customer = s.Customer
AND s.Product IN ('A', 'B')
HAVING
COUNT(DISTINCT s.Product) = 2
)
GROUP BY
t.Customer;
Select customerid from table group by customerid having product like 'A' and product like 'B' or
you can try having count(distinct product) =2this seems to be more accurate.
The whole idea is in a group of customerid suppose 1 if I have several A's and B's count(distinct product) will give as 2 else it will be 1 so the answer is as above.
Another way I just figured out was
SELECT CustomerID
FROM table
WHERE product in ('a','b')
GROUP BY customerid
HAVING sum(case product ='a' then 1 else 0 end) > 0
and sum(case when product ='b' then 1 else 0 end) > 0

Show duplicate rows(all columns of that row) where all columns are duplicate except one column

In below table, I need to select duplicate records where all columns are duplicate except Customer Type and Price for a particular week.
For e.g
Week Customer Product Customer Type Price
1 Alex Cycle Consumer 100
1 Alex Cycle Reseller 101
2 John Motor Consumer 200
3 John Motor Consumer 200
3 John Motor Reseller 201
I am using below query but this query doesn't show me both costumer type, it just shows me consumer count(*) for a combination.
select Week, Customer, product, count(distinct Customer Type)
from table
group by Week, Customer, product
having count(distinct Customer Type) > 1
I would like to see below result, that shows me duplicate values and not just the count(*) of duplicate row. I am trying to see customers assigned to multiple customer types in a particular week for a product and at the same time show me all columns. It doesn't matter if the price is different.
Week Customer Product Customer Type Price
1 Alex Cycle Consumer 100
1 Alex Cycle Reseller 101
3 John Motor Consumer 200
3 John Motor Reseller 201
Thanks
Shaki
WITH CustomerDistribution_CTE (WeekC ,CustomerC, ProductC)
AS
(
select Week, Customer, product
from Your_Table_Name group by Week, Customer,
product having count(distinct CustomerType) > 1
)
SELECT Y.*
FROM CustomerDistribution_CTE C
inner join Your_Table_Name Y on C.WeekC =Y.Week
and C.CustomerC =Y.Customer and C.productC =Y.product
Note :Please replace "Your_Table_Name" with exact table name and Try.
One way to achieve this, using generic SQL, is to use a "derived table" like this:
select x.*
from tablex x
inner join (
select Week, Customer, Product
from tablex
group by Week, Customer, Product
having count(*) > 1
) d on x.Week = d.Week and x.Customer = d.Customer and x.Product = d.Product
You can do that by using DISTINCT like
select DISTINCT Customer,Product,Customer_Type,Price from Your_Table_Name
will look for DISTINCT combination.
Note: This query if of SQL Server
From the expected result that you have pasted, it looks like you are not concerned about the week.
If you have a ID (incremental PK), it would be much simpler like below
select * from table where ID not in
(select max(ID) from table group by Customer, Product, CustomerType having count(*) > 1 )
This is tested on MySQL. Do you have a ID column?
In case you don't have a ID column, try the below:
select max(week) week, Customer, Product, CustomerType, max(price) from device group by Customer, Product, CustomerType;
I have not verified this one.
This will return your expected result set:
select *
from table
-- Teradata syntax to filter the result of an OLAP-function
-- (similar to HAVING after GROUP BY)
qualify
count(*)
over (partition by Week, Customer, product) > 1
For other DBMSes you will need to nest your query:
select *
from
(
select ...,
count(*)
over (partition by Week, Customer, product) as cnt
from table
) as dt
where cnt > 1
Edit:
After re-reading your description above Select might be not exactly what you want, because it will also return rows with a single type. Then switch to:
select *
from table
-- Teradata syntax to filter the result of an OLAP-function
-- (similar to HAVING after GROUP BY)
qualify -- at least two different types:
min(Customer_Type) over (partition by Week, Customer, product)
<> max(Customer_Type) over (partition by Week, Customer, product)

Is it possible to create and use window function in the same query?

I'm using PostgreSQL and I have the following situation:
table of Sales (short version):
itemid quantity
5 10
5 12
6 1
table of stock (short version):
itemid stock
5 30
6 1
I have a complex query that also needs to present in one of it's columns the SUM of each itemid.
So it's going to be:
Select other things,itemid,stock, SUM (quantity) OVER (PARTITION BY itemid) AS total_sales
from .....
sales
stock
This query is OK. however this query will present:
itemid stock total_sales
5 30 22
6 1 1
But I don't need to see itemid=6 because the whole stock was sold. meaning that I need a WHERE condition like:
WHERE total_sales<stock
but I can't do that as the total_sales is created after the WHERE is done.
Is there a way to solve this without surrounding the whole query with another one? I'm trying to avoid it if I can.
You can use a subquery or CTE:
select s.*
from (Select other things,itemid,stock,
SUM(quantity) OVER (PARTITION BY itemid) AS total_sales
from .....
) s
where total_sales < stock;
You cannot use table aliases defined in a SELECT in the SELECT, WHERE, or FROM clauses for that SELECT. However, a subquery or CTE gets around this restriction.
You can also use an inner select in your WHERE statement like this:
SELECT *, SUM (quantity) OVER (PARTITION BY itemid) AS total_sales
FROM t
WHERE quantity <> (SELECT SUM(quantity) FROM t ti WHERE t.itemid = ti.itemid);

DISTINCT for only one column

Let's say I have the following query.
SELECT ID, Email, ProductName, ProductModel FROM Products
How can I modify it so that it returns no duplicate Emails?
In other words, when several rows contain the same email, I want the results to include only one of those rows (preferably the last one). Duplicates in other columns should be allowed.
Clauses like DISTINCT and GROUP BY appear to work on entire rows. So I'm not sure how to approach this.
If you are using SQL Server 2005 or above use this:
SELECT *
FROM (
SELECT ID,
Email,
ProductName,
ProductModel,
ROW_NUMBER() OVER(PARTITION BY Email ORDER BY ID DESC) rn
FROM Products
) a
WHERE rn = 1
EDIT:
Example using a where clause:
SELECT *
FROM (
SELECT ID,
Email,
ProductName,
ProductModel,
ROW_NUMBER() OVER(PARTITION BY Email ORDER BY ID DESC) rn
FROM Products
WHERE ProductModel = 2
AND ProductName LIKE 'CYBER%'
) a
WHERE rn = 1
This assumes SQL Server 2005+ and your definition of "last" is the max PK for a given email
WITH CTE AS
(
SELECT ID,
Email,
ProductName,
ProductModel,
ROW_NUMBER() OVER (PARTITION BY Email ORDER BY ID DESC) AS RowNumber
FROM Products
)
SELECT ID,
Email,
ProductName,
ProductModel
FROM CTE
WHERE RowNumber = 1
When you use DISTINCT think of it as a distinct row, not column. It will return only rows where the columns do not match exactly the same.
SELECT DISTINCT ID, Email, ProductName, ProductModel
FROM Products
----------------------
1 | something#something.com | ProductName1 | ProductModel1
2 | something#something.com | ProductName1 | ProductModel1
The query would return both rows because the ID column is different. I'm assuming that the ID column is an IDENTITY column that is incrementing, if you want to return the last then I recommend something like this:
SELECT DISTINCT TOP 1 ID, Email, ProductName, ProductModel
FROM Products
ORDER BY ID DESC
The TOP 1 will return only the first record, by ordering it by the ID descending it will return the results with the last row first. This will give you the last record.
You can over that by using GROUP BY like this:
SELECT ID, Email, ProductName, ProductModel
FROM Products
GROUP BY Email
For Access, you can use the SQL Select query I present here:
For example you have this table:
CLIENTE|| NOMBRES || MAIL
888 || T800 ARNOLD || t800.arnold#cyberdyne.com
123 || JOHN CONNOR || s.connor#skynet.com
125 || SARAH CONNOR ||s.connor#skynet.com
And you need to select only distinct mails.
You can do it with this:
SQL SELECT:
SELECT MAX(p.CLIENTE) AS ID_CLIENTE
, (SELECT TOP 1 x.NOMBRES
FROM Rep_Pre_Ene_MUESTRA AS x
WHERE x.MAIL=p.MAIL
AND x.CLIENTE=(SELECT MAX(l.CLIENTE) FROM Rep_Pre_Ene_MUESTRA AS l WHERE x.MAIL=l.MAIL)) AS NOMBRE,
p.MAIL
FROM Rep_Pre_Ene_MUESTRA AS p
GROUP BY p.MAIL;
You can use this to select the maximum ID, the correspondent name to that maximum ID , you can add any other attribute that way. Then at the end you put the distinct column to filter and you only group it with that last distinct column.
This will bring you the maximum ID with the correspondent data, you can use min or any other functions and you replicate that function to the sub-queries.
This select will return:
CLIENTE|| NOMBRES || MAIL
888 || T800 ARNOLD || t800.arnold#cyberdyne.com
125 || SARAH CONNOR ||s.connor#skynet.com
Remember to index the columns you select and the distinct column must have not numeric data all in upper case or in lower case, or else it won't work.
This will work with only one registered mail as well.
Happy coding!!!
The reason DISTINCT and GROUP BY work on entire rows is that your query returns entire rows.
To help you understand: Try to write out by hand what the query should return and you will see that it is ambiguous what to put in the non-duplicated columns.
If you literally don't care what is in the other columns, don't return them. Returning a random row for each e-mail address seems a little useless to me.
Try This
;With Tab AS (SELECT DISTINCT Email FROM Products)
SELECT Email,ROW_NUMBER() OVER(ORDER BY Email ASC) AS Id FROM Tab
ORDER BY Email ASC
Try this:
SELECT ID, Email, ProductName, ProductModel FROM Products WHERE ID IN (SELECT MAX(ID) FROM Products GROUP BY Email)

SQL Server : SUM() of multiple rows including where clauses

I have a table that looks something like the following :
PropertyID Amount Type EndDate
--------------------------------------------
1 100 RENT null
1 50 WATER null
1 60 ELEC null
1 10 OTHER null
2 70 RENT null
2 10 WATER null
There will be multiple items billed to a property, also billed multiple times. For example RENT could be billed to property #1 12 times (over a year), however the only ones I'm interested for are those with ENDDATE of null (in otherwords, current)
I would like to achieve :
PropertyId Amount
--------------------------
1 220
2 80
I have tried to do something like this :
SELECT
propertyId,
SUM() as TOTAL_COSTS
FROM
MyTable
However, in the SUM would I be forced to have multiple selects bringing back the current amount for each type of charge? I could see this becoming messy and I'm hoping for a much simpler solution
Any ideas?
This will bring back totals per property and type
SELECT PropertyID,
TYPE,
SUM(Amount)
FROM yourTable
GROUP BY PropertyID,
TYPE
This will bring back only active values
SELECT PropertyID,
TYPE,
SUM(Amount)
FROM yourTable
WHERE EndDate IS NULL
GROUP BY PropertyID,
TYPE
and this will bring back totals for properties
SELECT PropertyID,
SUM(Amount)
FROM yourTable
WHERE EndDate IS NULL
GROUP BY PropertyID
......
Try this:
SELECT
PropertyId,
SUM(Amount) as TOTAL_COSTS
FROM
MyTable
WHERE
EndDate IS NULL
GROUP BY
PropertyId
you mean getiing sum(Amount of all types) for each property where EndDate is null:
SELECT propertyId, SUM(Amount) as TOTAL_COSTS
FROM MyTable
WHERE EndDate IS NULL
GROUP BY propertyId
sounds like you want something like:
select PropertyID, SUM(Amount)
from MyTable
Where EndDate is null
Group by PropertyID
The WHERE clause is always conceptually applied (the execution plan can do what it wants, obviously) prior to the GROUP BY. It must come before the GROUP BY in the query, and acts as a filter before things are SUMmed, which is how most of the answers here work.
You should also be aware of the optional HAVING clause which must come after the GROUP BY. This can be used to filter on the resulting properties of groups after GROUPing - for instance HAVING SUM(Amount) > 0
Use a common table expression to add grand total row, top 100 is required for order by to work.
With Detail as
(
SELECT top 100 propertyId, SUM(Amount) as TOTAL_COSTS
FROM MyTable
WHERE EndDate IS NULL
GROUP BY propertyId
ORDER BY TOTAL_COSTS desc
)
Select * from Detail
Union all
Select ' Total ', sum(TOTAL_COSTS) from Detail