Can somebody please give me the correct query.
I have a table Item with two columns ItemNo and AliasItemNo.
I want to write a query to return me only those items that have more than one alias name
I'm trying to execute
select ItemNo, AliasItemNo
from Item
group by ItemNo, AliasItemNo
having count(ItemNo) > 1
order by ItemNo Asc
This query is not giving correct results.
Pls help
A subquery with the count is the way that works in all rdbms:
SELECT ItemNo, AliasItemNo FROM Item i1
WHERE (SELECT COUNT(*) FROM Item i2 WHERE i1.ItemNo=i2.ItemNo) > 1
ORDER BY ItemNo Asc
In SQL-Server >= 2005 you could also use COUNT(*) OVER (PARTITION BY ItemNo) in a CTE:
WITH CTE AS(
SELECT ItemNo,AliasItemNo,
Num=COUNT(*) OVER (PARTITION BY ItemNo)
FROM Item)
SELECT ItemNo, AliasItemNo FROM CTE
WHERE Num > 1
ORDER BY ItemNo Asc;
Demo: http://sqlfiddle.com/#!6/78d9c/9/1
This should work:
select ItemNo
from Item
group by ItemNo
having count(AliasItemNo) > 1
order by ItemNo Asc
You are counting ItemNo instead of AliasItemNo
There are a lot of ways to do this. Here's a couple of ways.
--using a sub query
SELECT
ItemNo,
AliasItemNo
FROM
Item INNER JOIN
(
SELECT
ItemNo
GROUP BY
ItemNo
HAVING
COUNT(AliasItemNo) > 1)
AS dupes
ON
Item.ItemNo = dupes.ItemNo
ORDER BY
Item.ItemNo ASC
--using CTE
;WITH
dupes AS
(
SELECT
ItemNo
GROUP BY
ItemNo
HAVING
COUNT(AliasItemNo) > 1
)
SELECT
ItemNo,
AliasItemNo
FROM
Item INNER JOIN dupes
ON
Item.ItemNo = dupes.ItemNo
ORDER BY
Item.ItemNo ASC
Related
I am having trouble with this query in MS Access for Office 365. For each grouping I am trying to select the Top 10 rows by ItemCount. So top 10 ItemName counts will be shown per GroupID. I know the below is wrong because it will only query ten records. I am not sure where to go from here. I tried a subquery that ordered the items then attempted to pull out top 10 items but I obviously did it wrong.
Select Top 10 GroupID, ItemName, COUNT(ItemName) as ItemCount
FROM Table
GROUP BY GROUPID, ItemName
Order By COUNT(ItemName) DESC;
Any suggestions appreciated
Thanks!
You can use a correlated subquery. Here is one method:
select GroupID, ItemName, count(*) as ItemCount
from Table as t
group by groupid, ItemName
having count(*) in (select top 10 count(*)
from table as t2
where t2.groupid = t.groupid
group by t2.groupid, t2.itemname
order by count(*) desc
)
order by count(*) desc;
Note: If there are ties, you might get more than 10 rows for a given group.
Could you please try below, use the TOP keyword in a sub query.
Select GroupID, ItemName, COUNT(ItemName) as ItemCount
FROM Table t1
WHERE ItemName in (
Select TOP 10 t2.ItemName
from Table t2
where t2.GroupID = t1.GroupID
order by count(t2.ItemName) desc, t2.ItemName
)
GROUP BY GROUPID, ItemName
Order By GROUPID, COUNT(ItemName) DESC;
I'm looking for a way to let me select all orders that have multiple distinct names within the same order-number, it looks like this:
order - name
111-Paul
112-Paula
113-John
113-John
113-Jessica
114-Eric
114-Eric
114-John
115-Zack
115-Zack
115-Zack
etc.
so that i would get all the orders that have 2 or more distinct names in it:
113-John
113-Jessica
114-Eric
114-John
with which I could do further queries but I'm stuck. Can anyone give me some hints on how to tackle this problem please? I've tried it with count(*) which looked like this:
select order, name, count(name) from dbo.orders
group by order, name
having count(name) > 1
which gave me all the orders which had more than 1 name in it but I don't know how to let it only show orders with the distinct names.
Here's one approach using exists:
select distinct [order], name
from orders o
where exists (
select 1
from orders o2
where o.[order] = o2.[order] and o.name != o2.name)
Fiddle Demo
I would use windows functions for this
For example:
select distinct order
from
(select
order,
row_number() over(partition by order, name order by order asc) as rn
) as t1
where rn > 1
you can do the same with count
count(*) over(partition by order,name order by order asc) as cnt
Here's a straight forward implementation in Sql Server:
select distinct *
from table1
where [order] in (
select [order]
from (select distinct * from table1) iq
group by [order]
having count(*) > 1)
It's essentially breaking down the problem into:
Finding the orders that have more than one distinct value.
Finding the pairs of distinct order - name that belong to the list previously calculated.
When you use HAVING COUNT(name) > 1, it is counting all of the rows in those groups, including duplicate rows (rows 113-John and 113-John are 2 rows for order 113). I would query distinct rows from your table, and then select from that:
SELECT [order], [name] FROM (
SELECT DISTINCT [order], [name] FROM dbo.orders
) A
GROUP BY [order], [name]
HAVING COUNT([name]) > 1
As a note, if a [name] is null, then it will not be counted with COUNT(name). If you want nulls to be counted, use COUNT(*) instead.
You can use count(distinct name) to get the number of unique names for each order:
select [order], count(distinct name)
from orders
group by [order]
To just get the order for those you can use having:
select [order]
from orders
group by [order]
having count(distinct name) > 1
To get the details for those orders you can put that in the where clause to just return the rows with order in that list:
select *
from orders
where [order] in (
select [order]
from orders
group by [order]
having count(distinct name) > 1
)
sqlfiddle
I would use RANK (or DENSE_RANK) for this as shown below.
SELECT [Order]
FROM (SELECT
[Order],
RANK() OVER(PARTITION BY [Order] ORDER BY Name) AS NameRank
FROM [StackOverflow].[dbo].[OrderAndName]) ranked
WHERE ranked.NameRank > 1
GROUP BY [Order]
The sub-query ranks (gives a seeding) to the names in an order according to their value. Names with the same value would have the same rank i.e. when an order has one name multiple times (like 115) the rank of all names would be 1.
The partition is important here as otherwise you would get the rank for all names for all orders which wouldn't give you the result you'd like.
It is then just a case of pulling out the orders that have a RANK greater than 1 and grouping (could use distinct if that's a preference).
You can then join to this table to get get the orders and names as follows;
SELECT oan.[Order], [Name]
FROM [StackOverflow].[dbo].[OrderAndName] oan
INNER JOIN (SELECT [Order]
FROM (SELECT [Order],
RANK() OVER(PARTITION BY [Order] ORDER BY Name) AS NameRank
FROM [StackOverflow].[dbo].[OrderAndName]) ranked
WHERE ranked.NameRank > 1
GROUP BY [Order]) twoOrMore ON oan.[Order] = twoOrMore.[Order]
I'm trying to get the most expensive and cheapest items from two different tables.
The output should be one row with the values for MostExpensiveItem, MostExpensivePrice, CheapestItem, CheapestPrice
I was able to get the price of the most expensive and cheapest items in the two tables with following query:
SELECT
MAX(ExtrasPrice) as MostExpensivePrice, MIN(ExtrasPrice) as CheapestPrice
FROM
(
SELECT ExtrasPrice FROM Extras
UNION ALL
SELECT ItemPrice FROM Items
) foo
How can I add the names of the items (ItemName, ExtrasName) to my output? Again, there should only be one row as the output.
Try this:
SELECT TOP 1 FIRST_VALUE(Price) OVER (ORDER BY Price) AS MinPrice,
FIRST_VALUE(Name) OVER (ORDER BY Price) AS MinName,
LAST_VALUE(Price) OVER (ORDER BY Price DESC) AS MaxPrice,
LAST_VALUE(Name) OVER (ORDER BY Price DESC) AS MaxName
FROM (
SELECT ExtrasName AS Name, ExtrasPrice AS Price FROM Extras
UNION ALL
SELECT ItemName As Name, ItemPrice AS Price FROM Items) u
SQL Fiddle Demo
TOP 1 with order by clause should work for you. Try this
SELECT *
FROM (SELECT TOP 1 ExtrasPrice,ExtrasName
FROM Extras ORDER BY ExtrasPrice Asc),
(SELECT TOP 1 ItemPrice,ItemName
FROM Items ORDER BY ItemPrice Desc)
Note: Comma can be replaced with CROSS JOIN
You can use row_number() for this. If you are satisfied with two rows:
SELECT item, price
FROM (SELECT foo.*, row_number() over (order by price) as seqnum_asc,
row_number() over (order by price) as seqnum_desc
FROM (SELECT item, ExtrasPrice as price FROM Extras
UNION ALL
SELECT item, ItemPrice FROM Items
) foo
) t
WHERE seqnum_asc = 1 or seqnum_desc = 1;
EDIT:
If you have an index on "price" in both tables, then the cheapest method is probably:
with exp as (
(select top 1 item, ExtrasPrice as price
from Extras e
order by price desc
) union all
(select top 1 i.item, ItemPrice
from Items i
order by price desc
)
),
cheap as (
(select top 1 item, ExtrasPrice as price
from Extras e
order by price asc
) union all
(select top 1 i.item, ItemPrice
from Items i
order by price asc
)
)
select top 1 *
from exp
order by price desc
union all
select top 1 *
from cheap
order by price asc;
If you want this in one row, you can replace the final query with:
select e.*, c.*
from (select top 1 *
from exp
order by price desc
) e cross join
(select top 1 *
from cheap
order by price asc
) c
This works perfectly until an order comes in where the stationID is not =2. My logic would be that sql searches where both conditions meet and display those results and not look where timeplaced is max then if stationid does not =2 display nothing which is what its doing.
SELECT OrderNo
FROM Orders
WHERE TimePlaced = (SELECT max(TimePlaced) FROM Orders)
AND StationID=2
Add your condition into the inner select too
SELECT OrderNo
FROM Orders
WHERE TimePlaced =
(
SELECT max(TimePlaced)
FROM Orders
WHERE StationID=2
)
AND StationID=2
But if you do not want to add the condition twice, then just "link" the inner select with the outer:
SELECT OrderNo
FROM Orders O
WHERE TimePlaced =
(
SELECT max(TimePlaced)
FROM Orders
WHERE StationID=O.StationID
)
AND StationID=2
Another way you could try would be with using a CTE and ROW_NUMBER:
;WITH OrdersCTE AS
(
SELECT *,
ROW_NUMBER() OVER(PARTITION BY StationID ORDER BY TimePlaced Desc) AS rn
FROM Orders
)
SELECT *
FROM OrdersCTE
WHERE rn = 1
AND StationID = 2
In SQL Server, suppose we have a SALES_HISTORY table as below.
CustomerNo PurchaseDate ProductId
1 20120411 12
1 20120330 13
2 20120312 14
3 20120222 16
3 20120109 16
... and many records for each purchase of each customer...
How can I write the appropriate query for finding:
For each customer,
find the product he bought at MOST,
find the percentage of this product over all products he bought.
The result table must have columns like:
CustomerNo,
MostPurchasedProductId,
MostPurchasedProductPercentage
Assuming SQL Server 2005+, you can do the following:
;WITH CTE AS
(
SELECT *,
COUNT(*) OVER(PARTITION BY CustomerNo, ProductId) TotalProduct,
COUNT(*) OVER(PARTITION BY CustomerNo) Total
FROM YourTable
), CTE2 AS
(
SELECT *,
RN = ROW_NUMBER() OVER(PARTITION BY CustomerNo
ORDER BY TotalProduct DESC)
FROM CTE
)
SELECT CustomerNo,
ProductId MostPurchasedProductId,
CAST(TotalProduct AS NUMERIC(16,2))/Total*100 MostPurchasedProductPercent
FROM CTE2
WHERE RN = 1
You still need to deal when you have more than one product as the most purchased one. Here is a sqlfiddle with a demo for you to try.
Could do a lot prettier, but it works:
with cte as(
select CustomerNo, ProductId, count(1) as c
from SALES_HISTORY
group by CustomerNo, ProductId)
select CustomerNo, ProductId as MostPurchasedProductId, (t.c * 1.0)/(select sum(c) from cte t2 where t.CustomerNo = t2.CustomerNo) as MostPurchasedProductPercentage
from cte t
where c = (select max(c) from cte t2 where t.CustomerNo = t2.CustomerNo)
SQL Fiddle