Equivalent to doing a join after the group by - sql-server

I am looking to merge the following two queries into one:
select top 100 date, count(*) from sections
where content not like '%some condition%'
group by date
order by date;
select top 100 date, count(*) from sections
group by date
order by date;
It is like this question, LEFT JOIN after GROUP BY? except that I need this to work for MS SQL Server, not MySQL (the difference being that MSSQL does not allow subqueries in the from clause).
I am looking for a way to have the result set have three columns, date, the first count(*), and the second count(*).
My current solution is:
select top 100 date,
(select count(*) from sections s1
where content not like '%some condition%'
and s1.date = s2.date),
(select count(*) from sections s1
where s1.date=s2.date) from sections as s2
group by date
order by date;
Is there a better way to do this?

Try this:
with q1 as (select top 100 date, count(*) total from sections
where content not like '%some condition%'
group by date),
q2 as (select top 100 date, count(*) total from sections
group by date)
select q1.date, q1.total total1, q2.total total2
from q1
join q2 on q1.date = q2.date
order by q1.date
UPDATE:
Or this:
select date,
count(*) total,
sum(has_condition) total_condition
from (select top 100
date,
case when content not like '%some condition%' then 1
else 0 end has_condition
from sections ) t
group by date
order by date;
I did not do any triout, but that is the idea.

This is the query that do the job with just one select:
select top 100 date,
count(*) as count_all,
sum (
case
when content not like '%some condition%' then 1
else 0
end
) as count_condition
from sections
group by date
order by date
I am also pasting a working snippet from AdventureWorks2012 database
select top 100
ModifiedDate,
count(*) as count_all,
sum (
case when CarrierTrackingNumber not like '4911%' then 1
else 0
end
) as count_condition
from [Sales].[SalesOrderDetail]
group by ModifiedDate
order by ModifiedDate
For you reference you can use subqueries after FROM clause in SQL Server.

Related

SQL - Return a value sum only once when grouped

I want to count the unique record of a string but grouping by dates, and if the string already appeared previously on a group it shouldn't be counted anymore.
I've tried using distinct and it does show the unique count of the record but the record is counted again on every month.
Actual and minified SQL query:
select
date,
count(distinct d.name) as count
from ...
group by date
Sample and desired output
Image
Grab unique names and tag them with the earliest date. At that point it's just a matter of regrouping the resulting rows by date. Each name will uniquely correspond to only one date as desired:
with data as (select name, min("date") as dt from T group by name)
select dt, count(name) as cnt from data group by dt;
If you still need to see the original dates even when no names are counted, then flag each row according to whether it should be counted and then count the flags per date:
with data as (
select *,
case when "date" = min("date") over (partition by name)
then 1 end as flag
from T
)
select "date", count(flag) as cnt
from data
group by "date";
So you want the name only count once:
SELECT COUNT(u.name) as name_count, u.[date]
FROM (
SELECT d.name,MIN(d.date) AS [date]
FROM yourTable d
GROUP BY d.name) u
GROUP BY u.[date];
You can add a ROW_NUMBER() that is Partitioned by name and ordered by date and add a WHERE clause that only returns the rows with Row_Number = 1.
You can check this following option-
SELECT A.Date,COUNT(B.[Name]) Count
FROM
(
SELECT DISTINCT Date FROM your_table
)A
LEFT JOIN
(
SELECT * FROM
(
SELECT *,ROW_NUMBER() OVER(PARTITION BY [Name] ORDER BY Date) RN
FROM your_table
)A WHERE RN = 1
)B ON A.Date = B.Date
GROUP BY A.Date
But the best option if I modify a bit the concept from Shawnt00 is as below-
SELECT A.Date,COUNT(B.[Name]) Count
FROM
(
SELECT DISTINCT Date FROM your_table
)A
LEFT JOIN
(
SELECT [Name],MIN(Date) Date FROM your_table GROUP BY [Name]
)B ON A.Date = B.Date
GROUP BY A.Date
Both case the output will be-
Date Count
20190101 2
20190201 0
20190301 1

How to select highest common value across groups

`Suppose I have a set of data with 2 fields - Type and Date. I am interested in finding (if exists) the the max common date across the various types. Is this easier to do in SQL or LINQ?
Given the data below the result should be 2018-02-01 as this is the max common date for all types. It there is no such date then no data is returned.
Type, Date
---------
1,2018-03-01
1,2018-02-01
1,2018-01-01
2,2018-02-01
2,2018-05-01
2,2018-01-01
3,2018-01-01
3,2018-03-01
3,2018-02-01
You could use:
SELECT TOP 1 [Date], COUNT(*) OVER(PARTITION BY Date) AS cnt
FROM tab
ORDER BY cnt DESC, [Date] DESC
DBFiddle Demo
This'll work if you have an unlimited or indeterminable number of Types:
CREATE TABLE #Sample ([Type] int, [DAte] date);
INSERT INTO #Sample
VALUES
(1,'20180301'),
(1,'20180201'),
(1,'20180101'),
(2,'20180201'),
(2,'20180501'),
(2,'20180101'),
(3,'20180101'),
(3,'20180301'),
(3,'20180201');
GO
WITH EntryCount AS(
SELECT [Type], [Date],
COUNT(*) OVER (PARTITION By [Date]) AS Entries
FROM #Sample)
SELECT MAX(Date)
FROM EntryCount EC
WHERE Ec.Entries = (SELECT COUNT(DISTINCT sq.[Type]) FROM #Sample sq);
GO
DROP TABLE #Sample;
Not sure how quick it'll be either though.
Example
Select Top 1 [Date]
from YourTable
Group By [Date]
Order By count([Type]) desc,[Date] desc
Returns
2018-02-01
This is not going to be very efficient not matter how you slice it because you have to compare across three groups. Assuming you have 3 types you could use a self join. Something like this.
select MAX(YourDate)
from YourTable yt
join YourTable yt2 on yt2.YourType = 2 and yt.YourDate = yt2.YourDate
join YourTable yt3 on yt3.YourType = 3 and yt.YourDate = yt3.YourDate
where yt.YourType = 1

SQL select top 10 for each year

I have a fact database from which I want to make a trendline based on top 10 items based on sum quantity for each item per year.
I've done the following, but it does for example select more than 10 entities for my year 2007:
select TOP 10 sum(Quantity) as Quantity,DIM_Time.Year, DIM_Item.Name as Name
from Fact_Purchase
join DIM_Item on DIM_Item.BKey_ItemId = Fact_Purchase.DIM_Item
join DIM_Time on DIM_Time.ID = Fact_Purchase.DIM_Time_DeliveryDate
where Fact_Purchase.DIM_Company = 2 and DIM_Time.ID = FACT_Purchase.DIM_Time_DeliveryDate
Group by dim_item.Name, DIM_Time.Year
Order by Quantity DESC
How do I select top 10 items with the highest quantity through all my years, with only 10 top entities for each year?
As you can guess, the company is individual, and Is going to be a parameter in my report
I think this is what you're going for. My apologies if I messed up on translating your tables across.
select *
from (
select DIM_Time.[Year], dim_item.Name, SUM(Quantity) Quantity, RANK() OVER (PARTITION BY DIM_Time.[Year] ORDER BY SUM(Quantity) DESC) salesrank
from Fact_Purchase
join DIM_Item on DIM_Item.BKey_ItemId = Fact_Purchase.DIM_Item
join DIM_Time on DIM_Time.ID = Fact_Purchase.DIM_Time_DeliveryDate
where Fact_Purchase.DIM_Company = 2 and DIM_Time.ID = FACT_Purchase.DIM_Time_DeliveryDate
group by dim_item.Name, DIM_Time.[Year]
) tbl
where salesrank <= 10
order by [Year], salesrank
The subquery groups by name/year, and the RANK() OVER part sets up a sort of row index that increments by SUM(Quantity) and restarts for each Year. From there you just have to filter out anything with a salesrank (index) that's over 10.
SELECT
_year,
Name,
_SUM,
RANK_iD
FROM
(
SELECT
_year,
Name,
_SUM,
DENSE_RANK()OVER(PARTITION BY _year,_Month ORDER BY _SUM DESC) AS RANK_iD
FROM(
Select
DIM_Time AS _year,
DIM_Item as Name,
sum(Quantity) AS _SUM
from
#ABC
GROUP BY
_year,
Name
)A
)B
WHERE RANK_iD<=10

Select and count statement

I am new and learning SQL on Microsoft SQL Server 2008. I am trying to do the following for a table of order line items each record is one order item.
any transaction numbers with only one record and a code of #####.
I have tried many ways but haven't been able to figure it out
Select * from Table
where count(transactionnumber)<2 and Code='9987'
I think I got it. Had to use a different code because QA didn't have an distinct rows with that code. Let me know if you see anything that might cause and issue.
Select * from (select Orders.TransactionNumber from Orders
group by Orders.TransactionNumber
having COUNT (Orders.TransactionNumber)=1) as transa
Inner join Orders on transa.TransactionNumber=Orders.TransactionNumber
where ItemCode=9803
Ended up with this code
use XXX
Select Orders.TransactionNumber,Orders.RepNumber, Orders.CustomerID,Orders.ShipToId,orders.ItemCode,Orders.Quantity,Orders.ReceivedDate,Orders.TransmitStatus from (select TransactionNumber from Orders
group by TransactionNumber
having COUNT (TransactionNumber)=1) as transa
Inner join Orders on Orders.TransactionNumber=transa.TransactionNumber
where ItemCode=9987 and ReceivedDate > DateADD (day, -1, GetDate() )
IF ##ROWCOUNT > 0
BEGIN
EXEC msdb.dbo.sp_send_dbmail
recipients=N'XXXX',
#body='Merchandisers orders with only Item Code 9803',
#subject ='only Item Code 9803',
#profile_name ='',
#query = 'Select Orders.TransactionNumber,Orders.RepNumber,Orders.CustomerID,Orders.ShipToId,orders.ItemCode,Orders.Quantity,Orders.ReceivedDate,Orders.TransmitStatus from(select TransactionNumber from Orders
group by TransactionNumber
having COUNT (TransactionNumber)<2) as transa
Inner join Orders on Orders.TransactionNumber=transa.TransactionNumber
where ItemCode=9803 and ReceivedDate > DateADD (day, -1, GetDate() )'
END'
Your query should include something to group by (Name, TransactionNumber, etc.) do a count and constrain on that.
SELECT TransactionCode, COUNT(*)
FROM TABLE
GROUP BY TransactionCode
HAVING COUNT(*) < 2
AND ItemCode = 9987
You may consider using the OVER keyword, so that you don't need to muck around the count(*) and group by so much. That way you can see counts without grouping.
Select *, count(*) over (partition by transactioncode) as "TheCounts"
from table
--where itemcode = 9903
or
Select *, count(*) over (partition by transactioncode, itemcode) as "TheCounts"
from table
--where itemcode = 9903
Here is what I ended up with on this that seems to be working correctly for about a week now it executes a sp_send_dbmail only if there are records.
Select Orders.TransactionNumber, Orders.RepNumber, Orders.CustomerID,Orders.ShipToId,orders.ItemCode,Orders.Quantity,Orders.ReceivedDate,Orders.TransmitStatus from (select TransactionNumber from Orders
group by TransactionNumber
having COUNT (TransactionNumber)=1) as transa
Inner join Device_Orders on Orders.TransactionNumber=transa.TransactionNumber
where ItemCode=9987 and ReceivedDate > DateADD (day, -1, GetDate() )
IF ##ROWCOUNT > 0

Running total query in select statement without views

I have to query a set of running total data by month.
e.g.
Month Amount Total
2014-01-01 100 100
2014-01-02 100 200
2014-01-03 100 300
The application does not allow to create a view or SP. It is able to select data from a table directly.
e.g.
select Month,
Amount,
Total -- This is my problem.
from Table -- This is a table only.
Any ideas are welcome, thank you.
You can use OUTER APPLY:
SELECT T.Month,T.Amount,T2.Total
FROM Table1 T
OUTER APPLY
( SELECT Total = SUM(Amount)
FROM Table1 T2
WHERE T2.Month <= T.Month
) T2;
Or a correlated subquery:
SELECT T.Amount,
( SELECT Amount = SUM(Amount)
FROM Table1 T2
WHERE T2.Month <= T.Month
)
FROM Table1 T
The easiest way is to use SQL Server 2012 because it has cumulative sum built-in:
select Month, Amount,
sum(Amount) over (order by Month) as Total -- This is my problem.
from Table;
The correlated subquery method follows a similar structure:
select Month, Amount,
(select sum(Amount) from table t2 where t2.Month <= t.Month) as Total
from Table t;
These are usually the two methods that I would consider, because both are standard SQL. As Vignesh points out you can do it with cross apply as well (although as I write this, his query is not correct).
Here is a second way to create a running total:
SELECT t.month, t.amount,
SUM(t.amount) OVER(PARTITION BY t.month ORDER BY t.month
ROWS BETWEEN UNBOUNDED PRECEDING AND CURRENT ROW) as [Total]
FROM [yourTable] AS t

Resources