SQL Server : how to group only part of the syntax - sql

I have a problem creating a SQL Server query.
In summary, the query should get columns that are sum and count, grouped by customerID, and another column that is a case when by a column that is not used as a grouper column.
My problem is to group only part of the syntax, while the case when column does not need to be grouped.
A sample data, Test:
customerID, 1,2,3,4...
InvoiceID, 1234551, 1234552...
ProductID, A, B, C...
Date, Datetime
Income, int
customerID
InvoiceID
ProductID
Date
Income
1
1234551
A
01/01/2015
300
2
1234552
B
02/01/2016
300
I have a solution, but I am sure there is a more simple solution.
SELECT DISTINCT
Test.CustomerId,
ISNULL(TBL.Income_2015, 0) AS Income_2015,
ISNULL(TBL_2.Income_2016, 0) AS Income_2016,
CASE
WHEN Test.ProductID = 'A'
THEN 'TRUE'
ELSE 'FALSE'
END AS 'purchase_product_A',
TBL_3.Invoices
FROM
Test
LEFT OUTER JOIN
(SELECT CustomerId, SUM(Income) AS Income_2015
FROM Test
WHERE YEAR(Date) = 2015
GROUP BY CustomerId) TBL ON Test.customerID = TBL.customerID
LEFT OUTER JOIN
(SELECT CustomerId, SUM(Income) AS Income_2016
FROM Test
WHERE YEAR(Date) = 2016
GROUP BY CustomerId) TBL_2 ON Test.customerID = TBL_2.customerID
LEFT OUTER JOIN
(SELECT CustomerId, COUNT(InvoiceID) AS Invoices
FROM Test
GROUP BY CustomerId) TBL_3 ON Test.customerID = TBL_3.customerID
To produce:
customerID, 1,2,3...
Income_2015, int
Income_2016, int
Invoices, int
Purchase_product_A, boolean
customerID
Income_2015
Income_2016
Invoices
Purchase_product_A
1
300
300
2
TRUE
10
0
400
1
FALSE
Thanks!
Nir

You may use conditional aggregation with a single pass query:
SELECT
CustomerId,
SUM(CASE WHEN YEAR(Date) = 2015 THEN Income ELSE 0 END) AS Income_2015,
SUM(CASE WHEN YEAR(Date) = 2016 THEN Income ELSE 0 END) AS Income_2016,
COUNT(InvoiceID) AS Invoices,
CASE WHEN COUNT(CASE WHEN ProductID = 'A' THEN 1 END) > 0
THEN 'TRUE' ELSE 'FALSE' END AS [Purchase_product_A]
FROM Test
GROUP BY
CustomerId;

Related

Sharing row in a union - group by?

I have a query like this:
SELECT * FROM (
SELECT id, SUM( orderLineTotal ) as orderLine1, NULL as orderLine 2
FROM Orders
WHERE Orders.Date < #Today
GROUP BY id
UNION
SELECT id, NULL as orderLine1, SUM(orderLineTotal2) as orderLine 2
FROM Orders
WHERE Orders.Date = #Today
GROUP BY id
) o
GROUP BY o.id, o.orderLine1, o.orderLine2
I'm getting back a result like this:
ID OrderLine1 OrderLine2
-----------------------------------------
1 105.00 NULL
1 NULL 204.00
2 49.30 NULL
2 NULL 94.24
Is there any way to modify the query to return something like this?
ID OrderLine1 OrderLine2
-----------------------------------------
1 105.00 204.00
2 49.30 94.24
You want to do this with one query and conditional aggregation:
SELECT id,
SUM(case when Orders.Date < #Today then orderLineTotal end) as orderLine1,
SUM(case when Orders.Date = #Today then orderLineTotal2 end) as orderLine2
FROM Orders
GROUP BY id
By the way, are there really two columns, orderLineTotal and orderLineTotal2? I suspect there is only one and the second sum() should change accordingly.

How to combine two selects with different where clauses?

select kota ,total, totalsum from
(
SELECT i.[Antam_Unit] as kota ,count(p.[Id_Pks_Pk])as total FROM [SIMPKBL].[dbo].[Pks_Pk] p join [SIMPKBL].[dbo].[Par_Unit_Antam] i on i.Id_Unit_Antam = p.Id_Unit_Antam group by i.[Id_Unit_Antam],i.Antam_Unit
UNION ALL
SELECT i.[Antam_Unit] as kota , count(p.[Id_Proposal_Pk])as totalsum FROM [SIMPKBL].[dbo].[Pks_Pk] p join [SIMPKBL].[dbo].[Par_Unit_Antam] i on i.Id_Unit_Antam = p.Id_Unit_Antam where YEAR(p.Tanggal_Cetak_Pks_Pk) = '2012' group by i.[Id_Unit_Antam],i.Antam_Unit
) t
group by kota,total
I want an output like this:
kota total totalsum
A 12 4
B 16 5
Since the queries are so similar it appears that they can be combined into one select:
select i.[Antam_Unit] as kota,
count(p.[Id_Pks_Pk]) as total,
count( case when YEAR(p.Tanggal_Cetak_Pks_Pk) = '2012' then p.[Id_Proposal_Pk] else null end ) as totalsum
FROM [SIMPKBL].[dbo].[Pks_Pk] p join [SIMPKBL].[dbo].[Par_Unit_Antam] i on i.Id_Unit_Antam = p.Id_Unit_Antam
group by i.[Id_Unit_Antam], i.Antam_Unit
If I understood your requirements correctly, the result you are looking for can be achieved using this simple statement:
SELECT i.[Antam_Unit] as kota,
count(p.[Id_Pks_Pk]) as total,
SUM(CASE WHEN YEAR(p.Tanggal_Cetak_Pks_Pk) = '2012' AND p.[Id_Proposal_Pk] IS NOT NULL THEN 1 ELSE 0 END) as totalsum
FROM [SIMPKBL].[dbo].[Pks_Pk] p
join [SIMPKBL].[dbo].[Par_Unit_Antam] i
on i.Id_Unit_Antam = p.Id_Unit_Antam
group by i.[Id_Unit_Antam],i.Antam_Unit

SQL select and Group by

I have a table in SQL like this.
OrderID ItemID ItemPrice ItemType
1 A 100 1
1 B 50 1
1 C 10 0
2 A 100 1
2 F 60 0
3 G 10 0
So I want to get out put like this?
OrderID ItemPrice -Type= 1 ItemPrice -Type= 0
1 150 10
2 10 60
3 10
Do you have any idea about the SQL command to use?
I think it is group by order ID and Item type.
What you are doing is a pivot transformation. There are a few ways to do it, but my favorite way is using CASE inside SUM:
SELECT
OrderId,
SUM(CASE WHEN ItemType = 0 THEN ItemPrice ELSE 0 END) AS Type0_Price,
SUM(CASE WHEN ItemType = 1 THEN ItemPrice ELSE 0 END) AS Type1_Price
FROM MyTable
GROUP BY OrderId
This scales nicely if you have more than two types. All you have to do is add another SUM(...) line in your select list without having to change the rest of the query.
I think this will perform well, since the calculations in the SELECT list can be done without incurring additional row scans or lookups. That's the downside of self-joins and sub-selects.
Try this::
Select
DISTINCT(orderId),
(Select SUM(ITEMPRICE) from table where Itemtype=1 group by ORDERID) as ItemType1,
(Select SUM(ITEMPRICE) from table where Itemtype=0 group by ORDERID) as ItemType0
from table
Untested, but this should work for you.
SELECT t1.OrderID,
ItemPrice-Type1 = SUM(t1.ItemPrice),
ItemPrice-Type2 = SUM(t2.ItemPrice)
FROM TableName t1
INNER JOIN TableName t2 on t1.OrderID = t2.OrderID and t1.ItemID = t2.ItemID
WHERE t1.ItemType = 1 AND t2.ItemType = 0
GROUP BY t1.OrderID
Did this work?:
SELECT
OrderID,
SUM(ItemPrice*ItemType) Type1,
SUM(ItemPrice*(1-ItemType)) Type0
FROM
TableName
GROUP BY OrderID

Find percentage of products against total customers

I have got sql table which has 2 columns userid and products, userid is unique while products will have 3 values[0,1,-1] -1 represents no products bought, 1 represents product currently
being used, while 0 represents product has been used.
What i want is a percentage query for total users in the table
product used (%): ?
products currently in use (%): ?
product not sold(%): ?
Any suggestion or assistance will be highly appreciated
Thanks
What I have tried so far?
Select count(userId) * 100 / (select count(products) Where products = 1) AS perc
from product group by userid
Try this:
SELECT SUM(CASE WHEN products = 1 THEN 1 ELSE 0 END) * 100 /
Count(DISTINCT Products.UserID) AS 'Currently used products%',
SUM(CASE WHEN products = 0 THEN 1 ELSE 0 END) * 100 /
Count(DISTINCT Products.UserID) As 'Used products%',
SUM(CASE WHEN products = -1 THEN 1 ELSE 0 END) * 100 /
Count(DISTINCT Products.UserID) AS 'Not sold products%'
FROM Products
SELECT SUM(CASE WHEN Products = 1 THEN 1 ELSE 0 END) * 100
/ COUNT(DISTINCT UserID) AS product_currently_in_use_pct
, SUM(CASE WHEN Products = 0 THEN 1 ELSE 0 END) * 100
/ COUNT(DISTINCT UserID) AS product_used_pct
, SUM(CASE WHEN Products = -1 THEN 1 ELSE 0 END) * 100
/ COUNT(DISTINCT UserID) AS product_not_sold_pct
FROM MyTable
You can try like this also.
FIDDLE
select count(*) total
,count(p1.products)/count(*)*100 as currentlyused
,count(p2.products)/count(*)*100 as used
,count(p3.products)/count(*)*100 as not_sold
from products p
left join products p1 on p1.products=1 and p1.id=p.id
left join products p2 on p2.products=0 and p2.id=p.id
left join products p3 on p3.products=-1 and p3.id=p.id
SELECT products,
(convert(numeric(5,2),count(products))/(SELECT convert(numeric(5,2),count(products)) FROM product)) * 100
AS "Percentage"
FROM product GROUP BY products

Sum different row in column based on second column value

I have an Orders table (simplified)
OrderId,
SalesPersonId,
SaleAmount,
CurrencyId,
...
I am attempting to create a report on this table, I'm hoping for something like:
SalesPersonId TotalCAD TotalUSD
1 12,345.00 6,789.00
2 7,890.00 1,234.00
I'd prefer not to do a self join (perhaps I'm optimizing prematurely, but this seems inefficient) IE:
SELECT SalesPersonId, SUM(OrdersCAD.SaleAmount), SUM(OrderUSD.SaleAmount)
FROM Orders
LEFT JOIN Orders AS OrdersCAD ON Orders.OrderID AND Orders.CurrencyID = 1
LEFT JOIN Orders AS OrdersUSD ON Orders.OrderID AND Orders.CurrencyID = 2
But I cannot think of another way to do this, any ideas?
Use a CASE block:
SELECT
SalesPersonId,
SUM(
CASE CurrencyID
WHEN 1 THEN SaleAmount
ELSE 0
END
) AS TotalCAD,
SUM(
CASE CurrencyID
WHEN 2 THEN SaleAmount
ELSE 0
END
) AS TotalUSD
FROM Orders
GROUP BY SalesPersonId
Try This:
SELECT SalesPersonId,
SUM(CASE WHEN CurrencyID = 1 THEN SaleAmount ELSE 0 END) as CAD,
SUM(CASE WHEN CurrencyID = 2 THEN SaleAmount ELSE 0 END) as USD
FROM ORDERS
Consider trying out a scalar-valued function (SQL Server 2000 or later).
CREATE FUNCTION dbo.GetOrdersSumByCurrency
(
#SalesPersonID INT, #CurrencyID INT
)
RETURNS DECIMAL(10, 2)
AS
BEGIN
DECLARE #Sum DECIMAL(10, 2)
SELECT #Sum = ISNULL(SUM(SalesAmount), 0) FROM dbo.Orders
WHERE SalespersonID=#SalesPersonID AND CurrencyID = #CurrencyID
RETURN #Sum
END
Then execute SQL such as this to get the results (assumes you also have a separate salespersons table, or otherwise use instead SELECT DISTINCT SalesPersonId.... FROM Orders) :
SELECT SalesPersonId,
dbo.GetOrdersSumByCurrency(SalesPersonId, 1) AS SumUSD, dbo.GetOrdersSumByCurrency(SalesPersonId, 2) AS SumCAD
FROM SalesPersons
Be sure to run query plans to see if it performs as you need compared against the other possibilities suggested here, especially if you are processing a large quantity of data.