SQL To delete number of items is less than required item number - sql

I have two tables - StepModels (support plan) and FeedbackStepModels (feedback), StepModels keeps how many steps each support plan requires.
SELECT [SupportPlanID],COUNT(*)AS Steps
FROM [StepModels]
GROUP BY SupportPlanID
SupportPlanID (Steps)
-------------------------------
1 4
2 9
3 3
4 10
FeedbackStepModels keeps how many steps employee entered the system
SELECT [FeedbackID],SupportPlanID,Count(*)AS StepsNumber
FROM [FeedbackStepModels]
GROUP BY FeedbackID,SupportPlanID
FeedbackID SupportPlanID
---------------------------------------------
1 1 3 --> this suppose to be 4
2 2 9 --> Correct
3 3 0 --> this suppose to be 3
4 4 10 --> Correct
If submitted Feedback steps total is less then required total amount I want to delete this wrong entry from the database. Basically i need to delete FeedbackID 1 and 3.
I can load the data into List and compare and delete it, but want to know if we can we do this in SQL rather than C# code.

You can use the query below to remove your unwanted data by SQL Script
DELETE f
FROM FeedbackStepModels f
INNER JOIN (
SELECT [FeedbackID],SupportPlanID, Count(*) AS StepsNumber
FROM [FeedbackStepModels]
GROUP BY FeedbackID,SupportPlanID
) f_derived on f_derived_FeedbackID=f.FeedBackID and f_derived.SupportPlanID = f.SupportPlanID
INNER JOIN (
SELECT [SupportPlanID],COUNT(*)AS Steps
FROM [StepModels]
GROUP BY SupportPlanID
) s_derived on s_derived.SupportPlanID = f.SupportPlanID
WHERE f_derived.StepsNumber < s_derived.Steps

I think you want something like this.
DELETE FROM [FeedbackStepModels]
WHERE FeedbackID IN
(
SELECT a.FeedbackID
FROM
(
SELECT [FeedbackID],
SupportPlanID,
COUNT(*) AS StepsNumber
FROM [FeedbackStepModels]
GROUP BY FeedbackID,
SupportPlanID
) AS a
INNER JOIN
(
SELECT [SupportPlanID],
COUNT(*) AS Steps
FROM [StepModels]
GROUP BY SupportPlanID
) AS b ON a.SupportPlanID = b.[SupportPlanID]
WHERE a.StepsNumber < b.Steps
);

Related

How to update table by of the records in the table

I have a table in PostgerSQL and I need to make N entries in the table twice and for the first half I need to fill in the partner_id field with the value 1 and the second half with the value partner_id = 2.
i try to `
update USERS_TABLE set user_rule_id = 1;
update USERS_TABLE set user_rule_id = 2 where USERS_TABLE.id > count(*)/2;
`
I depends a lot how precise the number of users have to be that are updated with 1 or 2.
The following would be quite unprecise,a s it doesn't take the exact number of user that already exist8after deleting some rows the numbers doesn't fit anymore.
SELECT * FROM USERS_TABLE
id
user_rule_id
1
1
2
1
3
2
4
2
5
2
SELECT 5
If you have a lot of deleted rows and want still the half of the users, you can choose following approach, which does rely on the id, but at teh actual row number
UPDATE USERS_TABLE1
set user_rule_id = CASE WHEN rn <= (SELECT count(*) FROM USERS_TABLE1)/ 2 then 1
ELSE 2 END
FROM (SELECT id, ROW_NUMBER() OVER( ORDER BY id) rn FROM USERS_TABLE1) t
WHERE USERS_TABLE1.id = t.id;
UPDATE 5
SELECT * FROM USERS_TABLE1
id
user_rule_id
1
1
2
1
3
2
4
2
5
2
SELECT 5
fiddle
In the sample case it it the same result, but when you have a lot of rows and a bunch of the deleted users, the senind will give you quite a good result

SQL COUNT with condition and without - using JOIN

My goal is something like following table:
Key | Count since date X | Count total
1 | 4 | 28
With two simple selects I could gain this values: (the key of the table consists of 3 columns [t$ncmp, t$trav, t$seqn])
1. SELECT COUNT(*) FROM db.table WHERE t$date >= sysdate-2 GROUP BY t$ncmp, t$trav, t$seqn
2. SELECT COUNT(*) FROM db.table GROUP BY t$ncmp, t$trav, t$seqn
How can I join these statements?
What I tried:
SELECT n.t$trav, COUNT(n.t$trav), m.total FROM db.table n
LEFT JOIN (SELECT t$ncmp, t$trav, t$seqn, COUNT(*) as total FROM db.table
GROUP BY t$ncmp, t$trav, t$seqn) m
ON (n.t$ncmp = m.t$ncmp AND n.t$trav = m.t$trav AND n.t$seqn = m.t$seqn)
WHERE n.t$date >= sysdate-2
GROUP BY n.t$ncmp, n.t$trav, n.t$seqn
I tried different variantes, but always got errors like 'group by is missing' or 'unknown qualifier'.
Now this at least executes, but total is always 2.
T$TRAV COUNT(N.T$TRAV) TOTAL
4 2 2
29 3 2
51 1 2
62 2 2
16 1 2
....
If it matter, I will run this as an OPENQUERY from MSSQLSERVER to Oracle-DB.
I'd try
GROUP BY n.t$trav, m.total
You typically GROUP BY the same columns as you SELECT - except those who are arguments to set functions.
My goal is something like following table:
If so, you seem to want conditional aggregation:
select key, count(*) as total,
sum(case when datecol >= date 'xxxx-xx-xx' then 1 else 0 end) as total_since_x
from t
group by key;
I'm not sure how this relates to your sample queries. I simply don't see the relationship between that code and your question.

Count different groups in the same query

Imagine I have a table like this:
# | A | B | MoreFieldsHere
1 1 1
2 1 3
3 1 5
4 2 6
5 2 7
6 3 9
B is associated to A in an 1:n relationship. The table could've been created with a join for example.
I want to get both the total count and the count of different A.
I know I can use a query like this:
SELECT v1.cnt AS total, v2.cnt AS num_of_A
FROM
(
SELECT COUNT(*) AS cnt
FROM SomeComplicatedQuery
WHERE 1=1
-- AND SomeComplicatedCondition
) v1,
(
SELECT COUNT(A) AS cnt
FROM SomeComplicatedQuery
WHERE 1=1
-- AND SomeComplicatedCondition
GROUP BY A
) v2
However SomeComplicatedQuery would be a complicated and slow query and SomeComplicatedCondition would be the same in both cases. And I want to avoid calling it unnessesarily. Aside from that if the query changes, you need to make sure to change it in the other place too, making it prone to error and creating (probably unnessesary) work.
Is there a way to do this more efficiently?
Are you looking for this?
SELECT COUNT(*) AS total, COUNT(DISTINCT A) AS num_of_A
FROM (. . . ) q

Find duplicates within a specific period

I have a table with the following structure
ID Person LOG_TIME
-----------------------------------
1 1 2012-05-21 13:03:11.550
2 1 2012-05-22 13:09:37.050 <--- this is duplicate
3 1 2012-05-28 13:09:37.183
4 2 2012-05-20 15:09:37.230
5 2 2012-05-22 13:03:11.990 <--- this is duplicate
6 2 2012-05-24 04:04:13.222 <--- this is duplicate
7 2 2012-05-29 11:09:37.240
I have some application job that fills this table with data.
There is a business rule that each person should have only 1 record in every 7 days.
From the above example, records # 2,5 and 6 are considered duplicates while 1,3,4 and 7 are OK.
I want to have a SQL query that checks if there are records for the same person in less than 7 days.
;WITH cte AS
(
SELECT ID, Person, LOG_TIME,
DATEDIFF(d, MIN(LOG_TIME) OVER (PARTITION BY Person), LOG_TIME) AS diff_date
FROM dbo.Log_time
)
SELECT *
FROM cte
WHERE diff_date BETWEEN 1 AND 6
Demo on SQLFiddle
Please see my attempt on SQLFiddle here.
You can use a join based on DATEDIFF() to find records which are logged less than 7 days apart:
WITH TooClose
AS
(
SELECT
a.ID AS BeforeID,
b.ID AS AfterID
FROM
Log a
INNER JOIN Log b ON a.Person = b.Person
AND a.LOG_TIME < b.LOG_TIME
AND DATEDIFF(DAY, a.LOG_TIME, b.LOG_TIME) < 7
)
However, this will include records which you don't consider "duplicates" (for instance, ID 3, because it is too close to ID 2). From what you've said, I'm inferring that a record isn't a "duplicate" if the record it is too close to is itself a "duplicate".
So to apply this rule and get the final list of duplicates:
SELECT
AfterID AS ID
FROM
TooClose
WHERE
BeforeID NOT IN (SELECT AfterID FROM TooClose)
Please take a look at this sample.
Reference: SQLFIDDLE
Query:
select person,
datediff(max(log_time),min(log_time)) as diff,
count(log_time)
from pers
group by person
;
select y.person, y.ct
from (
select person,
datediff(max(log_time),min(log_time)) as diff,
count(log_time) as ct
from pers
group by person) as y
where y.ct > 1
and y.diff <= 7
;
PERSON DIFF COUNT(LOG_TIME)
1 1 3
2 8 3
PERSON CT
1 3
declare #Count int
set #count=(
select COUNT(*)
from timeslot
where (( (TimeFrom<#Timefrom and TimeTo >#Timefrom)
or (TimeFrom<#Timeto and TimeTo >#Timeto))
or (TimeFrom=#Timefrom or TimeTo=#Timeto)))

How do I fix this SQL query returning improper values?

I am writing an SQL query which will return a list of auctions a certain user is losing, like on eBay.
This is my table:
bid_id bid_belongs_to_auction bid_from_user bid_price
6 7 1 15.00
8 7 2 19.00
13 7 1 25.00
The problematic area is this (taken from my full query, placed at the end of the question):
AND EXISTS (
SELECT 1
FROM bids x
WHERE x.bid_belongs_to_auction = bids.bid_belongs_to_auction
AND x.bid_price > bids.bid_price
AND x.bid_from_user <> bids.bid_from_user
)
The problem is that the query returns all the auctions on which there are higher bids, but ignoring the user's even higher bids.
So, an example when the above query works:
bid_id bid_belongs_to_auction bid_from_user bid_price
6 7 1 15.00
7 7 2 18.00
In this case, user 1 is returned as losing the auction, because there is another bid higher than the users bid.
But, here is when the query doesn't work:
bid_id bid_belongs_to_auction bid_from_user bid_price
6 7 1 15.00
8 7 2 19.00
13 7 1 25.00
In this case, user 1 is incorrectly returned as losing the auction, because there is another bid higher than one of his previous bids, but the user has already placed a higher bid over that.
If it's important, here's my full query, but I think it won't be necessary to solve the aforementioned problem, but I'm posting it here anyway:
$query = "
SELECT
`bid_belongs_to_auction`,
`auction_unixtime_expiration`,
`auction_belongs_to_hotel`,
`auction_seo_title`,
`auction_title`,
`auction_description_1`
FROM (
SELECT
`bid_belongs_to_auction`,
`bid_from_user`,
MAX(`bid_price`) AS `bid_price`,
`auctions`.`auction_enabled`,
`auctions`.`auction_unixtime_expiration`,
`auctions`.`auction_belongs_to_hotel`,
`auctions`.`auction_seo_title`,
`auctions`.`auction_title`,
`auctions`.`auction_description_1`
FROM `bids`
LEFT JOIN `auctions` ON `auctions`.`auction_id`=`bids`.`bid_belongs_to_auction`
WHERE `auction_enabled`='1' AND `auction_unixtime_expiration` > '$time' AND `bid_from_user`='$userId'
AND EXISTS (
SELECT 1
FROM bids x
WHERE x.bid_belongs_to_auction = bids.bid_belongs_to_auction
AND x.bid_price > bids.bid_price
AND x.bid_from_user <> bids.bid_from_user
)
GROUP BY `bid_belongs_to_auction`
) AS X
WHERE `bid_from_user`='$userId'
";
Here's a different approach:
$query = "
SELECT
`max_bids`.`bid_belongs_to_auction`,
`auctions`.`auction_unixtime_expiration`,
`auctions`.`auction_belongs_to_hotel`,
`auctions`.`auction_seo_title`,
`auctions`.`auction_title`,
`auctions`.`auction_description_1`
FROM `auctions`
INNER JOIN (
SELECT
`bid_belongs_to_auction`,
MAX(`bid_price`) AS `auction_max_bid`,
MAX(CASE `bid_from_user` WHEN '$userId' THEN `bid_price` END) AS `user_max_bid`
FROM `bids`
GROUP BY `bid_belongs_to_auction`
) AS `max_bids` ON `auctions`.`auction_id` = `max_bids`.`bid_belongs_to_auction`
WHERE `auctions`.`auction_enabled`='1'
AND `auctions`.`auction_unixtime_expiration` > '$time'
AND `max_bids`.`user_max_bid` IS NOT NULL
AND `max_bids`.`user_max_bid` <> `max_bids`.`auction_max_bid`
";
Basically, when you are retrieving the max bids for all the auctions, you are also retrieving the specific user's max bids along. Next step is to join the obtained list to the auctions table and apply an additional filter on the user's max bid being not equal to the auction's max bid.
Note: the `max_bids`.`user_max_bid` IS NOT NULL condition might be unnecessary. It would definitely be so in SQL Server, because the non-nullness would be implied by the `max_bids`.`user_max_bid` <> `max_bids`.`auction_max_bid` condition. I'm not sure if it's the same in MySQL.
Untested, but this is how I would approach it. Ought to perform OK if there's an index on userid and also one on auctionid.
select OurUserInfo.auctionid, OurUserInfo.userid,
OurUserInfo.ourusersmaxbid, Winningbids.TopPrice
from
(
select A.auctionid, A.userid, max(A.price) as OurUsersMaxBid
from auctions A where userid = ?
group by A.auctionid, A.userid
) as OurUserInfo
inner join
(
-- get the current winning bids for all auctions in which our user is bidding
select RelevantAuctions.auctionid, max(auctions.price) as TopPrice
from auctions inner join
(
select distinct auctionid from auctions where userid = ? -- get our user's auctions
) as RelevantAuctions
on auctions.auctionid = RelevantAuctions.auctionid
group by RelevantAuctions.auctionid
) as WinninBids
on OurUserInfo.auctionid = winningbids.auctionid
where WinninBids.TopPrice > OurUserInfo.ourusersmaxbid
Instead of
SELECT 1
FROM bids x
WHERE x.bid_belongs_to_auction = bids.bid_belongs_to_auction
AND x.bid_price > bids.bid_price
AND x.bid_from_user <> bids.bid_from_user
try this:
SELECT 1
FROM (SELECT BID_ID,
BID_BELONGS_TO_AUCTION,
BID_FROM_USER,
BID_PRICE
FROM (SELECT BID_ID,
BID_BELONGS_TO_AUCTION,
BID_FROM_USER,
BID_PRICE,
RANK ()
OVER (
PARTITION BY BID_BELONGS_TO_AUCTION, BID_FROM_USER
ORDER BY BID_PRICE DESC)
MY_RANK
FROM BIDS)
WHERE MY_RANK = 1) x
WHERE x.bid_belongs_to_auction = bids.bid_belongs_to_auction
AND x.bid_price > bids.bid_price
AND x.bid_from_user <> bids.bid_from_user;