Identify same amounts over different users - sql

Consider the following table Orders:
OrderID Name Amount
-----------------------
1 A 100
2 A 5
3 B 32
4 C 4000
5 D 701
6 E 32
7 F 200
8 G 100
9 H 12
10 I 17
11 J 100
12 J 100
13 J 11
14 A 5
I need to identify, for each unique 'Amount', if there are 2 or more users that have ordered that exact amount, and then list the details of those orders. So the desired output would be:
OrderID Name Amount
---------------------
1 A 100
8 G 100
11 J 100
12 J 100
3 B 32
6 E 32
please note that user A has ordered 2 x an order of 5 (order 2 and 14) but this shouldn't be in the output as it is within the same user. Only if another user would have made a order of 5, it should be in the output.
Can anyone help me out?

I would just use exists:
select o.*
from orders o
where exists (select 1
from orders o2
where o2.amount = o.amount and o2.name <> o.name
);

You can do :
select t.*
from table t
where exists (select 1 from table t1 where t1.amount = t.amount and t1.name <> t.name);

If you want only selected field then
SELECT Amount,name,
count(*) AS c
FROM TABLE
GROUP BY Amount, name
HAVING c > 1
ORDER BY c DESC
if you want full row
select * from table where Amount in (
select Amount, name from table
group by Amount, name having count(*) > 1)

Related

postgreSQL - fill in blank date rows per ID

I have a table which looks like this:
ID
money_earned
days_since_start
1
1000
1
1
2000
2
1
3000
4
1
2000
5
2
1000
1
2
100
3
I want that rows, without a days_since_start (which means that the money_earned column was empty that day) - will include all the days PER ID, and fill the money_earned with last known value, so it to look like this:
ID
money_earned
days_since_start
1
1000
1
1
2000
2
1
2000
3
1
3000
4
1
2000
5
2
1000
1
2
1000
2
2
100
3
I have tried to look up for something like that, but I don't even know what function does that...
thank you!
You can try to use CTE RECURSIVE with OUTER JOIN and LAG window function to make it.
WITH RECURSIVE CTE
AS
(
SELECT ID,MIN(days_since_start) min_num,MAX(days_since_start) max_num
FROM T
GROUP BY ID
UNION ALL
SELECT ID,min_num+1,max_num
FROM CTE
WHERE min_num+1 <= max_num
)
SELECT c.ID,
CASE WHEN t1.ID IS NULL THEN LAG(money_earned) OVER(PARTITION BY c.ID ORDER BY c.min_num) ELSE money_earned END,
c.min_num days_since_start
FROM CTE c
LEFT JOIN T t1
ON c.min_num = t1.days_since_start
AND c.ID = t1.ID
ORDER BY c.ID
sqlfiddle

SQL join query for a view with sum of columns across 3 tables

I have 3 tables as below
Table - travel_requests
id industry_id travel_cost stay_cost other_cost
1 2 1000 500 200
2 4 4000 100 200
3 5 3000 0 400
4 1 3000 250 100
5 1 200 100 75
Table - industry_tech_region
id industry_name
1 Auto
2 Aero
3 Machinery
4 Education
5 MTV
Table - industry_allocation
id industry_id allocation
1 1 500000
2 2 300000
3 3 500000
4 4 300000
5 5 500000
6 1 200000
I want to create a view which has 3 columns
industry_name, total_costs, total_allocation
I created a view as below
SELECT industry_tech_region.industry_name,
SUM(travel_requests.travel_cost + travel_requests.stay_cost + travel_requests.other_cost) AS total_cost,
SUM(industry_allocation.allocation) AS total_allocation
FROM industry_tech_region
INNER JOIN industry_allocation
ON industry_tech_region.id = industry_allocation.industry_id
INNER JOIN travel_requests
ON industry_tech_region.id = travel_requests.industry_id
GROUP BY industry_tech_region.industry_name
But the result I get is as below which is incorrect
industry_name total_cost total_allocation
Aero 1700 300000
Auto 7450 1400000 (wrong should be 3725 and 700000)
Education 4300 300000
MTV 3400 500000
This is probably happening because there are 2 entries for industry_id 1 in the travel_requests table. But they should be counted only once.
Please let me know how do we correct the view statement.
Also I want to add another column in view which is remaining_allocation which is difference of total_allocation and total_cost for each industry.
you shoud join the sum (and not sum the join)
select
a.industry_name
, t1.total_cost
, t2.total_allocation
from dbo.industry_tech_region a
left join (
select dbo.travel_requests.industry_id
, SUM(dbo.travel_requests.travel_cost + dbo.travel_requests.stay_cost + dbo.travel_requests.other_cost) AS total_cost
FROM bo.travel_requests
group by dbo.travel_requests.industry_id
) t1 on a.id = t1.industry_id
left join (
select dbo.industry_allocation.industry_id
, SUM(dbo.industry_allocation.allocation) AS total_allocation
from dbo.industry_allocation
group by dbo.industry_allocation.industry_id
) t2 on a.id = t2.industry_id
this happen because you have two entry for the industry_id 1 and then the row are joined two time if you use the subquery for aggreated the row this can't happen ...
I have used left join because seems that not all the industry_id match for the 3 tables ..
You can use this approach too (without the ORDER BY because views do not allow it).
;WITH q AS (
SELECT industry_id
, sum(allocation) AS total_allocation
FROM #industry_allocation
GROUP BY industry_id
)
SELECT #industry_tech_region.industry_name
, isnull(SUM(#travel_request.travel_cost
+ #travel_request.stay_cost
+ #travel_request.other_cost),0.0) AS total_cost
,q.total_allocation AS total_allocation
FROM #industry_tech_region
LEFT JOIN q ON #industry_tech_region.id = q.industry_id
LEFT JOIN #travel_request ON #industry_tech_region.id = #travel_request.industry_id
GROUP BY #industry_tech_region.industry_name,q.total_allocation
ORDER BY industry_name

How I solve this questions?

I tried everything but I can't solve it, someone can help me cause I would like to understand it for getting more experience.
You have to use GROUP BY command :
SELECT
id_unit, COUNT(*)
FROM
order_amount
GROUP BY
id_unit;
Results :
id_unit COUNT(*)
kg 40
m3 30
pal 30
After if you want to display for each order your script will be :
SELECT
m.id_order,
(select count(*) from order_amount m2 where m2.id_order = m.id_order and m2.id_unit = 'kg') as num_kg,
(select count(*) from order_amount m2 where m2.id_order = m.id_order and m2.id_unit = 'pal') as num_pal,
(select count(*) from order_amount m2 where m2.id_order = m.id_order and m2.id_unit = 'pal') as num_m3,
COUNT(*) as total
FROM
order_amount m
GROUP BY
id_order;
Results :
id_order num_kg num_pal num_m3 total
1 5 2 2 8
2 0 2 2 4
3 6 5 5 14
4 5 0 0 10
5 5 3 3 15
For more detail SQL Fiddle

how to Get only the rows which's D column hold nearest lowest number to the C column?

------------------------------------------
ID Name C D
------------------------------------------
1 AK-47 10 5
2 RPG 10 20
3 Mp5 20 15
4 Sniper 20 18
5 Tank 90 80
6 Space12 90 20
7 Rifle 90 110
8 Knife 90 85
Consider 1,2 ; 3,4 ; 5,6,7,8 are as separate groups
So i need to get the row group wise that which's D column holds the nearest lower number to the C column
So the Expected Result is :
------------------------------------------
ID Name C D
------------------------------------------
1 AK-47 10 5
4 Sniper 20 18
8 Knife 90 85
How can I achieve this ?
select t1.*
from your_table t1
join
(
select c, min(abs(c-d)) as near
from your_table
group by c
) t2 on t1.c = t2.c and abs(t1.c-t1.d) = t2.near
Here is the syntax for another way of doing this. This uses a cte and will only hit the base table once.
with MySortedData as
(
select ID, Name, C, D, ROW_NUMBER() over(PARTITION BY C order by ABS(C - D)) as RowNum
from Something
)
select *
from MySortedData
where RowNum = 1

How to declare a row as a Alternate Row

id Name claim priority
1 yatin 70 5
6 yatin 1 10
2 hiren 30 3
3 pankaj 40 2
4 kavin 50 1
5 jigo 10 4
7 jigo 1 10
this is my table and i want to arrange this table as shown below
id Name claim priority AlternateFlag
1 yatin 70 5 0
6 yatin 1 10 0
2 hiren 30 3 1
3 pankaj 40 2 0
4 kavin 50 1 1
5 jigo 10 4 0
7 jigo 1 10 0
It is sorted as alternate group of same row.
I am Using sql server 2005. Alternate flag starts with '0'. In my example First record with name "yatin" so set AlternateFlag as '0'.
Now second record has a same name as "yatin" so alternate flag would be '0'
Now Third record with name "hiren" is single record, so assign '1' to it
In short i want identify alternate group with same name...
Hope you understand my problem
Thanks in advance
Try
SELECT t.*, f.AlternateFlag
FROM tbl t
JOIN (
SELECT [name],
AlternateFlag = ~CAST(ROW_NUMBER() OVER(ORDER BY MIN(ID)) % 2 AS BIT)
FROM tbl
GROUP BY name
) f ON f.name = t.name
demo
You could use probably an aggregate function COUNT() and then HAVING() and then UNION both Table, like:
SELECT id, A.Name, Claim, Priority, 0 as AlternateFlag
FROM YourTable
INNER JOIN (
SELECT Name, COUNT(*) as NameCount
FROM YourTable
GROUP BY Name
HAVING COUNT(*) > 1 ) A
ON YourTable.Name = A.Name
UNION ALL
SELECT id, B.Name, Claim, Priority, 1 as AlternateFlag
FROM YourTable
INNER JOIN (
SELECT Name, COUNT(*) as NameCount
FROM YourTable
GROUP BY Name
HAVING COUNT(*) = 1 ) B
ON YourTable.Name = B.Name
Now, this assumes that the Names are unique meaning the names like Yatin for example although has two counts is only associated to one person.
See my SqlFiddle Demo
You can use Row_Number() function with OVER that will give you enumeration, than use the reminder of integer division it by 2 - so you'll get 1s and 0s in your SELECT or in the view.