I have say the following rows
Country Population
IE 30
IE 20
UK 15
DE 20
DE 10
UK 20
BE 5
So basically I want to net the values together only for IE and DE... the rest I just want the values
So this would sum them all ..
Select Country, Sum(Population) From CountryPopulation group by Country
and I can add a where clause to exclude all other countries except IE and DE... but I also want these in the result set but just not summed.
So the table above would look like this when summed
Country Population
IE 50 -- Summed
UK 15 -- Orginal Value
DE 30 -- Summed
UK 20 -- Orginal Value
BE 5 -- Orginal Value
Problem is I can’t get a sum if, or case to work as the query has to be aggregated by group by. Only other way I can thing on is to
Sum all the IE and DE and union it with the rest of the data..
Or
Maybe use a CTE
Is there a nice slick way of doing this....
Select Country, Sum(Population)
From CountryPopulation
group by case when Country in ('IE','DE')
then 'IE_DE'
else Country
end
declare #t table (Country char(2), Population int)
insert into #t (Country, Population) values
('IE',30),
('IE',20),
('UK',15),
('DE',20),
('DE',10),
('UK',20),
('BE',5 )
; With Ordered as (
select Country,Population,CASE
WHEN Country in ('IE','DE') THEN 1
ELSE ROW_NUMBER() OVER (ORDER BY Country)
END as rn
from #t
)
select Country,rn,SUM(Population)
from Ordered
group by Country,rn
Produces:
Country rn
------- -------------------- -----------
BE 1 5
DE 1 30
IE 1 50
UK 6 15
UK 7 20
The trick is to just introduce a unique value for each row, except for the IE and DE rows that all get a 1. If the source rows all, actually, already have such a unique value then the CTE can be simplified (or avoided, at the expense of having to place the CASE expression in the GROUP BY as well as the SELECT)
You could also use UNION ALL and divide this query into two:
SELECT P.country,
P.population
FROM (SELECT country,
Population = Sum(population)
FROM dbo.countrypopulation cp
WHERE country IN ( 'IE', 'DE' )
GROUP BY country
UNION ALL
SELECT country, population
FROM dbo.countrypopulation cp
WHERE country NOT IN ( 'IE', 'DE' )
) P
ORDER BY P.population DESC
Even if this is not so concise it is readable and efficient.
sql-fiddle
Related
I want to know who has the most friends from the app I own(transactions), which means it can be either he got paid, or paid himself to many other users.
I can't make the query to show me only those who have the max friends number (it can be 1 or many, and it can be changed so I can't use limit).
;with relationships as
(
select
paid as 'auser',
Member_No as 'afriend'
from Payments$
union all
select
member_no as 'auser',
paid as 'afriend'
from Payments$
),
DistinctRelationships AS (
SELECT DISTINCT *
FROM relationships
)
select
afriend,
count(*) cnt
from DistinctRelationShips
GROUP BY
afriend
order by
count(*) desc
I just can't figure it out, I've tried count, max(count), where = max, nothing worked.
It's a two columns table - "Member_No" and "Paid" - member pays the money, and the paid is the one who got the money.
Member_No
Paid
14
18
17
1
12
20
12
11
20
8
6
3
2
4
9
20
8
10
5
20
14
16
5
2
12
1
14
10
It's from Excel, but I loaded it into sql-server.
It's just a sample, there are 1000 more rows
It seems like you are massively over-complicating this. There is no need for self-joining.
Just unpivot each row so you have both sides of the relationship, then group it up by one side and count distinct of the other side
SELECT
-- for just the first then SELECT TOP (1)
-- for all that tie for the top place use SELECT TOP (1) WITH TIES
v.Id,
Relationships = COUNT(DISTINCT v.Other),
TotalTransactions = COUNT(*)
FROM Payments$ p
CROSS APPLY (VALUES
(p.Member_No, p.Paid),
(p.Paid, p.Member_No)
) v(Id, Other)
GROUP BY
v.Id
ORDER BY
COUNT(DISTINCT v.Other) DESC;
db<>fiddle
The goal is to rank the Movies table according to quantity in the Inventory table such that for each duplicate value, it skips the subsequent value so that the next non-duplicate value remains in its rightful position. Display MovieID, Latest Title, Price, and the Rank.
WhileMovieId ‘1’ from Movies table corresponds to MovieId ‘101’ of your Movie inventory table and so on.
These are the tables
Movies
MovieId
latest title
Price
1
Breaking Dawn
200.00
2
The Proposal
185.00
3
Iron Man 2
180.00
4
Up
180.00
5
The Karate Kid
190.00
6
How to train your Dragon
190.00
7
Spiderman 3
195.00
Movie Inventory
MovieId
Quantity
101
3
105
4
107
5
108
7
110
8
111
4
And this is my attempt at the code that is showing a lot of NULL
SELECT CASE
WHEN Movies.MovieId + 100 = MovieInventory.MovieID
THEN CAST(MovieInventory.MovieID AS INT)
END AS 'MovieId',
Movies.LatestTitle, Movies.Price,
DENSE_RANK() OVER (ORDER BY Movies.MovieId DESC) AS [Rank]
FROM Movies, MovieInventory WHERE MovieInventory.MovieID IS NOT NULL
GO
This is what you need.
Notes:
You need RANK not DENSE_RANK to achieve the result you want
You need to order by Quantity
Use proper JOIN syntax, not comma , joins
Use table aliases for better readability
The foreign and primary key relationships are weird: mi.MovieID appears to be varchar but when converted to int is 100 more than m.MovieID???
The calculation in the SELECT is not accessible to the JOIN conditions
Don't use apostrophes '' to quote column names
SELECT
mi.MovieId,
m.LatestTitle,
m.Price,
RANK() OVER (ORDER BY mi.Quantity DESC) AS [Rank]
FROM Movies m
JOIN MovieInventory mi ON TRY_CAST(mi.MovieID AS int) = m.MovieID + 100;
I have 2 tables
OrderDetails:
Id Name type Quantity
------------------------------------------
2009 john a 10
2009 john a 20
2010 sam b 25
2011 sam c 50
2012 sam d 30
ValueDetails:
Id Value
-------------------
2009 300
2010 500
2011 200
2012 100
I need to get an output which displays the data as such :
Id Name type Quantity Price
-------------------------------------------------
2009 john a 10
2009 john a 20 9000
2010 sam b 25
2011 sam c 50
2012 sam d 30 25500
The price is calculated by Value x Quantity and the sum of the values is displayed in the last repeating row of the given Name.
I tired to use sum and group by but I get only two rows. I need to display all 5 rows. How can I write this query?
You can use Row_Number with max of Row_Number to get this formatted sum
;with cte as (
select od.*, sm= sum( od.Quantity*vd.value ) over (partition by Name),
RowN = row_number() over(partition by Name order by od.id)
from #yourOrderDetails od
inner join #yourValueDetails vd
on od.Id = vd.Id
)
select Id, Name, Type, Quantity,
case when max(RowN) over(partition by Name) = row_number() over(partition by Name order by Id)
then sm else null end as ActualSum
from cte
Your input tables:
create table #yourOrderDetails (Id int, Name varchar(20), type varchar(2), Quantity int)
insert into #yourOrderDetails (Id, Name, type, Quantity) values
(2009 ,'john','a', 10 )
,(2009 ,'john','a', 20 ) ,(2010 ,'sam ','b', 25 )
,(2011 ,'sam ','c', 50 ) ,(2012 ,'sam ','d', 30 )
create table #yourValueDetails(Id int, Value Int)
insert into #yourValueDetails(Id, value) values
( 2009 , 300 ) ,( 2010 , 500 )
,( 2011 , 200 ) ,( 2012 , 100 )
SELECT a.ID,
a.Name,
a.Type,
a.quantity,
price = (a.quantity * b.price)
FROM OrderDetails a LEFT JOIN
ValueDetails b on a.id = b.id
This will put the price on every row. If you want to do a SUM by Id,Name and Type it's not going to show the individual records like you show them above. If you want to put a SUM on one of the lines that share the same Id, Name and Type then you'd need a rule to figure out which one and then you could probably use a CASE statement to decide on which line you want to show the SUM total.
I have a select * query which gives lots of row and lots of columns of results. I have an issue with duplicates of one column A when given the same value of another column B that I would like to only include one of.
Basically I have a column that tells me the "name" of object and another that tells me the "number". Sometimes I have an object "name" with more than one entry for a given object "number". I only want distinct "numbers" within a "name" but I want the query to give the entire table when this is true and not just these two columns.
Name Number ColumnC ColumnD
Bob 1 93 12
Bob 2 432 546
Bob 3 443 76
This example above is fine
Name Number ColumnC ColumnD
Bob 1 93 12
Bob 2 432 546
Bill 1 443 76
Bill 2 54 1856
This example above is fine
Name Number ColumnC ColumnD
Bob 1 93 12
Bob 2 432 546
Bob 2 209 17
This example above is not fine, I only want one of the Bob 2's.
Try it if you are using SQL 2005 or above:
With ranked_records AS
(
select *,
ROW_NUMBER() OVER(Partition By name, number Order By name) [ranked]
from MyTable
)
select * from ranked_records
where ranked = 1
If you just want the Name and number, then
SELECT DISTINCT Name, Number FROM Table1
If you want to know how many of each there are, then
SELECT Name, Number, COUNT(*) FROM Table1 GROUP BY Name, Number
By using a Common Table Expression (CTE) and the ROW_NUMBER OVER PARTION syntax as follows:
WITH
CTE AS
(
SELECT
*,
ROW_NUMBER() OVER (PARTITION BY Name, Number ORDER BY Name, Number) AS R
FROM
dbo.ATable
)
SELECT
*
FROM
CTE
WHERE
R = 1
WITH
CTE AS
(
SELECT
*,
ROW_NUMBER() OVER (PARTITION BY Plant, BatchNumber ORDER BY Plant, BatchNumber) AS R
FROM dbo.StatisticalReports WHERE dbo.StatisticalReports. \!"FermBatchStartTime\!" >= DATEADD(d,-90, getdate())
)
SELECT
*
FROM
CTE
WHERE
R = 1
ORDER BY dbo.StatisticalReports.Plant, dbo.StatisticalReports.FermBatchStartTime
I often find when I am pulling data for analysis, that I group the number of orders a customer has placed into ranges, such as:
1-2
3-5
6-9
10-12
13-15
I do this with a CASE function. However, when you get the query results, the order ranges will be listed like:
1-2
10-12
13-15
3-5
6-9
This easy to correct in Excel when you have 1 query and a few order range groups. However, when you're pulling many queries, it's a pain to correct this over and over.
What is the best way to pull a range and have it ordered correctly?
here's an example of the query I would write:
SELECT
OrderRange = CASE
WHEN COUNT(OrderID) BETWEEN 1 AND 5 THEN '1-5'
WHEN COUNT(OrderID) BETWEEN 6 AND 10 THEN '6-10'
WHEN COUNT(OrderID) > 10 THEN '10+'
ELSE 'Error'
END
FROM Orders
GROUP BY CASE
WHEN COUNT(OrderID) BETWEEN 1 AND 5 THEN '1-5'
WHEN COUNT(OrderID) BETWEEN 6 AND 10 THEN '6-10'
WHEN COUNT(OrderID) > 10 THEN '10+'
ELSE 'Error'
END
ORDER BY... ?
I'd keep a table of ranges, e.g. (indices not written)
CREATE TABLE Ranges (RangeSet int, MinVal int, MaxVal int, Name varchar(50));
and then e.g.
INSERT INTO ranges VALUES
(1,1,5,'1-5'),(1,6,10,'6-10'),(1,11,-1,'11+'),
(2,1,10,'1-10'),(2,11,20,'11-20'),(2,21,30,'21-30'),(2,31,-1,'31+');
you get the idea. Now you do something like (table and field names free fiction)
SELECT
CustomerID,
count(OrderID) AS OrderCount
FROM Orders
WHERE <whatever, e.g order_date BETWEEN ... AND ...>
GROUP BY CustomerID
HAVING OrderCount>0
as you'd normally would expect, but wrap it in a superquery joining to the Ranges table
SELECT
BaseView.CustomerID as CustomerID,
Ranges.Name as OrderRange
FROM (
SELECT
CustomerID,
count(OrderID) AS OrderCount
FROM Orders
WHERE <whatever, e.g order_date BETWEEN ... AND ...>
GROUP BY CustomerID
HAVING OrderCount>0
) AS BaseView
INNER JOIN Ranges ON
Ranges.RangeSet=<id-of-required-rangeset>
AND BaseView.OrderCount>=Ranges.MinVal
AND (BaseView.OrderCount<=Ranges.MaxVal OR Ranges.MaxVal=-1)
ORDER BY RangeSet.MinVal DESC
;
Now you just have to supply the RangeSet you want to apply, maybe creating a new one on occasion.
Disclaimer: This is a performance-killer
If I'm understanding you correctly you want the list of customers and order ranges ordered from least to highest. You should be able to do that by just ordering by the count(orderID)
SELECT CustomerID,
OrderRange = CASE
WHEN COUNT(OrderID) BETWEEN 1 AND 5 THEN '1-5'
WHEN COUNT(OrderID) BETWEEN 6 AND 10 THEN '6-10'
WHEN COUNT(OrderID) > 10 THEN '10+'
ELSE 'Error'
END ,
FROM Orders
GROUP BY CustomerID
order by count(orderid)
Results:
CustomerId OrderRange
CENTC 1-5
GROSR 1-5
LAZYK 1-5
...
ROMEY 1-5
VINET 1-5
ALFKI 6-10
CACTU 6-10
...
VICTE 6-10
WANDK 6-10
BLONP 10+
GREAL 10+
RICAR 10+
...
QUICK 10+
ERNSH 10+
SAVEA 10+