T-Sql syntax to RANK a field based on several criteria - sql-server

I have an SQL query in SQL Server 2014 that outputs the following (extract shown, real output is around 45,000 records):
ResaID Agency Sales MTH Market Property
235 Smith 500 February 2015 UK RAV
451 John 1600 February 2015 France PLN
258 Alan 800 January 2015 UK BLS
I need an SQL Query that will RANK the agency column based on the following criteria: MTH, Market and Property and give me the following output (fictitious ranking shown below):
ResaId Rank
235 10
451 2
258 9
I will then use a JOIN based on ResaID to join the "Rank output" with my initial query.
In simpler terms, the ranking of the Agency will need to be done after grouping MTH, Market and Property.
Can this be achieved using T-SQL syntax?
Edit: I want the ranking to be done based on the Sales amount.

Yes, you can write something like this:
SELECT *, RANK() OVER(PARTITION BY Agency, mht ORDER BY sales DESC)
FROM [yourTable]

Related

Query to find records with more than one date

I'm trying to build a query for a MS SQL database that will find records with more than one year but not the records with only one.
Lets say I have a car dealership and I have 1 Chevy from 2015 and 2 from 2017 then I would want to find Chevy 2015 1 and chevy 2017 2 but if I have a three Fords from 2018 and only 2018 then I don't want that at all.
I have tweeked with groups and joins but I don't get any where. So I need Select from table something. I'm leaning toward a pivot table but not sure what to do. Thanks for the help
MyTable Contents
Model year count
Chevy 2012 1
Chevy 2012 1
Chevy 2015 1
Ford 2018 1
Ford 2018 1
Ford 2018 1
Buick 2017 1
Lexus 2017 1
Lexus 2015 1
Desired Result Set
Chevy 2012 2
Chevy 2015 1
Lexus 2017 1
Lexus 2015 1
Because it has 2 different years for the model
The below query should help you. Need not hardcode model values.
Select T.Model,T.[year] ,count(T.[year])
from T
join (select distinct * from T) S on T.model = S.model and T.year!=S.year
group by T.Model,T.[year]
You need to use SUM function and group by on subquery,Because there might be Multiple count on count column. then join itself and distinct to exclude duplicate data.
Select distinct t1.*
from (
SELECT Model,[year] ,sum([count]) totle
FROM T
group by Model,[year]
) t1
inner join T t2 on t1.Model = t2.Model and t1.[year] !=t2.[year]
sqlfiddle:http://sqlfiddle.com/#!18/e8756/55
Note:[table],[year] are keyword in sql avoid naming it as column name

Join two tables depending on date sequence

In SQL Server 2008, I want to join two tables depending on date sequence. More specifically, I need to left join Payments table to Profiles table by the following rules:
UserId has to be matched.
Every record in Payments matches the record in Profiles with the closest Profiles.CreationDate before Payments.PayDate.
For a simplified example,
Table Payments:
UserId PayDate Amount
1 2012 400
1 2010 500
2 2014 600
Table Profiles:
UserId CreationDate Address
1 2009 NY
1 2015 MD
2 2007 NJ
2 2013 MA
3 2008 TX
Desired Result:
UserId CreationDate PayDate Amount Address
1 2009 2010 500 NY
1 2009 2012 400 NY
2 2013 2014 600 MA
It's guaranteed that a user have at least 1 Profiles record before he pays. Another restriction is that I not authorized to write anything into the database.
I idea is first left join Payments with Profiles, then within the record group matching each (UserId, PayDate) tuple, sort it by CreationDate, then select the last record. But I don't know how to implement it in SQL language, or are there any better ways to do this merge?
Use Outer Apply to do this.
SELECT py.UserId,
CreationDate,
PayDate,
Amount,
Address
FROM Payments py
OUTER APPLY (SELECT TOP 1 *
FROM Profiles pr
WHERE py.UserId = pr.UserId
and PayDate> CreationDate
ORDER BY CreationDate desc) cs
SQLFIDDLE DEMO

Dividing a value by number of days in a month in a date field in SQL table

I am working on an SQL query which performs some arithmetic division on the data in a SQL Table based on the value in a date column.
To elaborate I have the following SQL table Deals
id Date Type Quantity Price
3 2014-11-04 Sweet 2500 23
9 2014-12-04 Sweet 5000 30
10 2014-12-04 Midale 2500 25.4
11 2014-11-04 Sweet 5000 45
Now, I want to some arithmetic operations on Quantity column grouped by Type and Date.
I used the following query
SELECT
Type,
COUNT(Id) AS Trades,
SUM(Quantity ) AS M3,
ROUND((6.2898*SUM(Quantity ))/31,4) AS BBLperDay,
CAST(Date as date) AS TradeMonth,
ROUND(SUM(Quantity*Price)/Sum(Quantity),4) AS WeightedAverage
FROM Deals
GROUP BY
Type,CAST(Date as date)
The above query returns
Type Trades M3 BBLperDay TradeMonth
Sweet 2 7500 1521.7258 2014-11-04
Midale 1 2500 507.2419 2014-12-04
Sweet 1 5000 1014.4839 2014-12-04
The above results are correct but I hard coded number of days as 31 in the Date in the expression
6.2898*SUM(Quantity )/31 AS BBLperDay
Is there a way I can do just have a value taken from Date column based on the month. If the month is December in the Date column the value will automatically be 31 as number of days in december are 31.
To find no. of days in a month
Select DAY(DATEADD(DD,-1,DATEADD(MM,DATEDIFF(MM,-1,getdate()),0)))
or If you are using SQL SERVER 2012+
Select DAY(EOMONTH(getdate()))
Change your query like this.
SELECT
Type,
COUNT(Id) AS Trades,
SUM(Quantity ) AS M3,
ROUND((6.2898*SUM(Quantity ))/DAY(DATEADD(DD,-1,DATEADD(MM,DATEDIFF(MM,-1,TradeMonth),0))),4) AS BBLperDay,
CAST(Date as date) AS TradeMonth,
ROUND(SUM(Quantity*Price)/Sum(Quantity),4) AS WeightedAverage
FROM Deals
GROUP BY
Type,CAST(Date as date),TradeMonth

SQL Server query to non normalized table

I have an unnormalized table of customer orders. I want count how many products are sold and display it in table by type.
OnlineSalesKey SalesOrderNumber ProductKey
--------------------------------------------
1 20121018 778
2 20121018 774
3 20121018 665
4 20121019 772
5 20121019 778
9 20121019 434
10 20121019 956
11 20121020 772
12 20121020 965
15 20121020 665
16 20121020 778
17 20121021 665
My query:
SELECT
s.ProductKey, COUNT (*) As Purchased
FROM
Sales s
GROUP BY
s.ProductKey
Question #1.
That query does a job. But now I want display and take into account only those orders where more than one item is purchased. Not sure how do i do that in one query. Any ideas?
Question #2
Is it possible to normalize results and get back data separated by semi column?
20121018 | 778; 774; 665
Thanks!
You don't say which SQL database you're using, and there will be different, more-or-less efficient answers for each database. (Sorry, just noticed MSSQL is in the question title.)
Here's a solution that will work in all or most databases:
SELECT s.ProductKey, COUNT (*) As Purchased
FROM Sales s
WHERE SalesOrderNum IN
(SELECT SalesOrderNum FROM Sales GROUP BY SalesOrderNum HAVING COUNT(*) > 1)
GROUP BY s.ProductKey
This is not the most efficient, but should work across the most products.
Also, please note that you're using the terms normalized and unnormalized in reverse. The table you have is normalized, the results you want are de-normalized.
There is no standard SQL statement to get the de-normalized results you want using SQL alone, but some databases (MySQL and SQLite) provide the group_concat function to do just this.
Q1: Look at HAVING clause
display and take into account only those orders...
SELECT s.SalesOrderNumber, COUNT (*) As Purchased
FROM Sales s
GROUP BY s.SalesOrderNumber
HAVING COUNT(*) > 1
So we group by the orders, apply the condition in HAVING, then display the SalesOrderNumber in the SELECT clause.
Q2: Look at several group concatenation techniques.
MySQL:
SELECT s.SalesOrderNumber, GROUP_CONCAT(DISTINCT ProductKey
ORDER BY ProductKey SEPARATOR '; ')
FROM Sales s
GROUP BY s.SalesOrderNumber
SQL Server: See this answer to a duplicate question. Basically, using FOR XML.
SELECT s.ProductKey, COUNT (*) As Purchased
FROM
Sales s
GROUP BY s.ProductKey
having count(*) > 1
EDIT -
Answer 1 - To display products for which orders had more than one product purchased -
SELECT s.ProductKey, COUNT (*) As Purchased
FROM Sales s
WHERE SalesOrderNum in
(
select SalesOrderNum from Sales
group by SalesOrderNum having count(*) > 1
)
GROUP BY s.ProductKey
1) Try this one:
SELECT s.ProductKey, COUNT (*) As Purchased
FROM
Sales s
GROUP BY s.ProductKey
HAVING COUNT(*) > 1

T-SQL - Getting most recent date and most recent future date

Assume the table of records below
ID Name AppointmentDate
-- -------- ---------------
1 Bob 1/1/2010
1 Bob 5/1/2010
2 Henry 5/1/2010
2 Henry 8/1/2011
3 John 8/1/2011
3 John 12/1/2011
I want to retrieve the most recent appointment date by person. So I need a query that will give the following result set.
1 Bob 5/1/2010 (5/1/2010 is most recent)
2 Henry 8/1/2011 (8/1/2011 is most recent)
3 John 8/1/2011 (has 2 future dates but 8/1/2011 is most recent)
Thanks!
Assuming that where you say "most recent" you mean "closest", as in "stored date is the fewest days away from the current date and we don't care if it's before or after the current date", then this should do it (trivial debugging might be required):
SELECT ID, Name, AppointmentDate
from (select
ID
,Name
,AppointmentDate
,row_number() over (partition by ID order by abs(datediff(dd, AppointmentDate, getdate()))) Ranking
from MyTable) xx
where Ranking = 1
This usese the row_number() function from SQL 2005 and up. The subquery "orders" the data as per the specifications, and the main query picks the best fit.
Note also that:
The search is based on the current date
We're only calculating difference in days, time (hours, minutes, etc.) is ignored
If two days are equidistant (say, 2 before and 2 after), we pick one randomly
All of which could be adjusted based on your final requirements.
(Phillip beat me to the punch, and windowing functions are an excellent choice. Here's an alternative approach:)
Assuming I correctly understand your requirement as getting the date closest to the present date, whether in the past or future, consider this query:
SELECT t.Name, t.AppointmentDate
FROM
(
SELECT Name, AppointmentDate, ABS(DATEDIFF(d, GETDATE(), AppointmentDate)) AS Distance
FROM Table
) t
JOIN
(
SELECT Name, MIN(ABS(DATEDIFF(d, GETDATE(), AppointmentDate))) AS MinDistance
FROM Table
GROUP BY Name
) d ON t.Name = d.Name AND t.Distance = d.MinDistance

Resources