Sharing row in a union - group by? - sql

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.

Related

SQL Server : how to group only part of the syntax

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;

How to Count Distinct on Case When?

I have been building up a query today and I have got stuck. I have two unique Ids that identify if and order is Internal or Web. I have been able to split this out so it does the count of how many times they appear but unfortunately it is not providing me with the intended result. From research I have tried creating a Count Distinct Case When statement to provide me with the results.
Please see below where I have broken down what it is doing and how I expect it to be.
Original data looks like:
Company Name Order Date Order Items Orders Value REF
-------------------------------------------------------------------------------
CompanyA 03/01/2019 Item1 Order1 170 INT1
CompanyA 03/01/2019 Item2 Order1 0 INT1
CompanyA 03/01/2019 Item3 Order2 160 WEB2
CompanyA 03/01/2019 Item4 Order2 0 WEB2
How I expect it to be:
Company Name Order Date Order Items Orders Value WEB INT
-----------------------------------------------------------------------------------------
CompanyA 03/01/2019 4 2 330 1 1
What currently comes out
Company Name Order Date Order Items Orders Value WEB INT
-----------------------------------------------------------------------------------------
CompanyA 03/01/2019 4 2 330 2 2
As you can see from my current result it is counting every line even though it is the same reference. Now it is not a hard and fast rule that it is always doubled up. This is why I think I need a Count Distinct Case When. Below is my query I am currently using. This pull from a Progress V10 ODBC that I connect through Excel. Unfortunately I do not have SSMS and Microsoft Query is just useless.
My Current SQL:
SELECT
Company_0.CoaCompanyName
, SopOrder_0.SooOrderDate
, Count(DISTINCT SopOrder_0.SooOrderNumber) AS 'Orders'
, SUM(CASE WHEN SopOrder_0.SooOrderNumber IS NOT NULL THEN 1 ELSE 0 END) AS 'Order Items'
, SUM(SopOrderItem_0.SoiValue) AS 'Order Value'
, SUM(CASE WHEN SopOrder_0.SooParentOrderReference LIKE 'INT%' THEN 1 ELSE 0 END) AS 'INT'
, SUM(CASE WHEN SopOrder_0.SooParentOrderReference LIKE 'WEB%' THEN 1 ELSE 0 END) AS 'WEB'
FROM
SBS.PUB.Company Company_0
, SBS.PUB.SopOrder SopOrder_0
, SBS.PUB.SopOrderItem SopOrderItem_0
WHERE
SopOrder_0.SopOrderID = SopOrderItem_0.SopOrderID
AND Company_0.CompanyID = SopOrder_0.CompanyID
AND SopOrder_0.SooOrderDate > '2019-01-01'
GROUP BY
Company_0.CoaCompanyName
, SopOrder_0.SooOrderDate
I have tried using the following line but it errors on me when importing:
, Count(DISTINCT CASE WHEN SopOrder_0.SooParentOrderReference LIKE 'INT%' THEN SopOrder_0.SooParentOrderReference ELSE 0 END) AS 'INT'
Just so know the error I get when importing at the moment is syntax error at or about "CASE WHEN sopOrder_0.SooParentOrderRefer" (10713)
Try removing the ELSE:
COUNT(DISTINCT CASE WHEN SopOrder_0.SooParentOrderReference LIKE 'INT%' THEN SopOrder_0.SooParentOrderReference END) AS num_int
You don't specify the error, but the problem is probably that the THEN is returning a string and the ELSE a number -- so there is an attempt to convert the string values to a number.
Also, learn to use proper, explicit, standard JOIN syntax. Simple rule: Never use commas in the FROM clause.
count distinct on the SooOrderNumber or the SooParentOrderReference, whichever makes more sense for you.
If you are COUNTing, you need to make NULL the thing that your are not counting. I prefer to include an else in the case because it is more consistent and complete.
, Count(DISTINCT CASE WHEN SopOrder_0.SooParentOrderReference LIKE 'INT%' THEN SopOrder_0.SooParentOrderReference ELSE null END) AS 'INT'
Gordon Linoff is correct regarding the source of your error, i.e. datatype mismatch between the case then value else value end. null removes (should remove) this ambiguity - I'd need to double check.
Editing my earlier answer...
Even though it looks, as you say, like count distinct is not supported in Pervasive PSQL, CTEs are supported. So you can do something like...
This is what you are trying to do but it is not supported...
with
dups as
(
select 1 as id, 'A' as col1 union all select 1, 'A' union all select 1, 'B' union all select 2, 'B'
)
select id
,count(distinct col1) as col_count
from dups
group by id;
Stick another CTE in the query to de-duplicate the data first. Then count as normal. That should work...
with
dups as
(
select 1 as id, 'A' as col1 union all select 1, 'A' union all select 1, 'B' union all select 2, 'B'
)
,de_dup as
(
select id
,col1
from dups
group by id
,col1
)
select id
,count(col1) as col_count
from de_dup
group by id;
These 2 versions should give the same result set.
There is always a way!!
I cannot explain the error you are getting. You are mistakenly using single quotes for alias names, but I don't actually think this is causing the error.
Anyway, I suggest you aggregate your order items per order first and only join then:
SELECT
c.coacompanyname
, so.sooorderdate
, COUNT(*) AS orders
, SUM(soi.itemcount) AS order_items
, SUM(soi.ordervalue) AS order_value
, COUNT(CASE WHEN so.sooparentorderreference LIKE 'INT%' THEN 1 END) AS int
, COUNT(CASE WHEN so.sooparentorderreference LIKE 'WEB%' THEN 1 END) AS web
FROM sbs.pub.company c
JOIN sbs.pub.soporder so ON so.companyid = c.companyid
JOIN
(
SELECT soporderid, COUNT(*) AS itemcount, SUM(soivalue) AS ordervalue
FROM sbs.pub.soporderitem
GROUP BY soporderid
) soi ON soi.soporderid = so.soporderid
GROUP BY c.coacompanyname, so.sooorderdate
ORDER BY c.coacompanyname, so.sooorderdate;

SQL Statement to get grouped data from every 30 days, grouped by date

I have a table with the following two columns
ExportTime | Manufacturer
2009-11-16 21:30:10.000 | IBM
2009-11-16 21:30:05.000 | VMWare
2009-11-16 21:30:12.000 | HP
2009-11-17 21:30:10.000 | Dell
2009-11-17 21:30:05.000 | VMWare
2009-11-17 21:30:12.000 | VMWare
I'm trying to create a chart to visualize the percentage of VMs over time. So basically I want a SQL result that looks like this:
Date | Virtual | Physical
2009-11-16 | 1 | 2
2009-11-16 | 2 | 1
This is the base query I've started with that gives me all virtual and all physical
SELECT a.vmcount, b.physcount FROM (
SELECT count(*) as vmcount FROM ServerDataHistorical
WHERE Manufacturer LIKE 'VM%') a, (
SELECT count(*) as physcount FROM ServerDataHistorical
WHERE Manufacturer NOT LIKE 'VM%'
) b
I tried this:
SELECT DATEADD(dd,(DATEDIFF(dd,0,ServerDataHistorical.ExportTime)),0) as date, a.vmcount, b.physcount FROM (
SELECT count(*) as vmcount FROM ServerDataHistorical
WHERE Manufacturer LIKE 'VM%') a, (
SELECT count(*) as physcount FROM ServerDataHistorical
WHERE Manufacturer NOT LIKE 'VM%'
) b,
ServerDataHistorical
GROUP BY DATEADD(dd,(DATEDIFF(dd,0,ServerDataHistorical.ExportTime)),0)
And it tells me that vmcount is invalid because it's not contained in the group by clause. I also tried this:
Declare #myDate varchar
Set #myDate = '2009-11-16'
SELECT ExportTime, a.vmcount, b.physcount FROM (
SELECT count(*) as vmcount FROM ServerDataHistorical
WHERE Manufacturer LIKE 'VM%' AND ExportTime LIKE #myDate + '%') a, (
SELECT count(*) as physcount FROM ServerDataHistorical
WHERE Manufacturer NOT LIKE 'VM%' AND ExportTime LIKE #myDate + '%'
) b,
ServerDataHistorical
Group by ExportTime
I thought I'd just programatically step through dates and get the numbers, but this gives the same error as the last one.
Your basic query would be much simpler with conditional summation:
SELECT sum(case when sdh.Manufacturer like 'VM%' then 1 else 0 end) as vmcount,
sum(case when sdh.Manufacturer not like 'VM%' then 1 else 0 end) as physical
FROM ServerDataHistorical sdh
If you want this by date, just add in a group by clause:
SELECT cast(sdh.ExportTime as date) as thedate,
sum(case when sdh.Manufacturer like 'VM%' then 1 else 0 end) as vmcount,
sum(case when sdh.Manufacturer not like 'VM%' then 1 else 0 end) as physical
FROM ServerDataHistorical sdh
group by cast(sdh.ExportTime as date)
order by 1;
And, if you want this for a particular date period, just add in a where clause:
where ExportTime >= cast(#myDate as datetime) and
ExportTime < cast(#MyDate as datetime) + 1
By the way, the variable #MyDate should be declared as a date or datetime. Get out of the habit of storing date/time values in strings.

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