how to delete column didn`t repeat in same table - sql

i have this sample data :-
===============
pageid||SiteID
===============
61 || 1
62 || 1
63 || 1
62 || 2
and
this table refrence to other table for pages , pageid unique in other
table
i need to delete data from this table and the referencing table ....but if the record repeat with other site id i must not delete it , when i select with site id all pageid will delete ( i can delete all pageid but 62 i want keept it in page table )

Use NOT EXISTS clause to prevent delete of pageid used in different siteID
Try this:
DELETE FROM yourtable
WHERE NOT EXISTS(
SELECT 'OTHERSITE'
FROM yourtable T2
WHERE T2.pageid = yourtable.pageid
AND T2.siteID <> yourtable.siteID)

DELETE FROM #Page_Site
WHERE siteID=#siteID
AND pageID not
in(
SELECT pageID
FROM #Page_Site p2
WHERE p2.siteID!=#siteID
);
I'm not certain how you use your query, so I formatted it as a parametered query. Otherwise, you simply have to replace both instances of #siteID with a any given value.

I like the way that SQL Server allows window functions with delete and update. So, it is also possible to use these functions for this purpose:
with todelete as (
select s.*, min(siteId) over (partition by pageid) as mins,
max(siteId) over (partition by pageid) as maxs
from sample s
)
delete from todelete
where mins = maxs;

You'll need to do that in 2 queries. First delete all the linking id's, secondly delete the pages itself
delete from pageSiteLinks
where pageid in (
select pageid
from pageSiteLinks
group by pageid
having count(SiteID) = 1
);
delete from pages
where pageid not in (
select pageid
from pageSiteLinks
);

Related

How to round robin by UUID in SQL database?

I have a list of agents that I want to assign tasks to using round robin.
agents table:
id
uuid1
uuid2
uuid3
uuid4
How to get rows of the table above in a round robin fashion?
Desired outcome:
uuid1 -> uuid2 -> uuid3 -> uuid4 -> uuid1 (repeat)
I tried ordering uuids then selecting the next one based on the previous uuid
SELECT id FROM agents ORDER BY id; // When there is no previous
SELECT id FROM agents WHERE id > 'uuid1' ORDER BY id; // After the first query
But I don't know how to repeat when I reach the last uuid (when uuid4 is retrieved and uuid1 must be selected again)
select nxt
from (
select
-- for every id, find the next item, if not exists, use the earliest (min)
id, coalesce(lead(id) over (order by id),(select min(id) from Tbl)) as nxt
from Tbl
) as Agents
where id = 'uuid4'
I don't have PostgreSQL handy right now, but this works under SQL Server and I don't see an obvious reason why it shouldn't under any other DBMS:
COALESCE(
(SELECT TOP 1 id FROM agents WHERE #current_id < id ORDER BY id),
(SELECT TOP 1 id FROM agents ORDER BY id)
)
So it simply tries to get the next id (the first argument of COALESCE), and if there is no next id gets the first id (the second argument of COALESCE).
Here is a full T-SQL demo, something similar can probably be done under PostgreSQL...
CREATE TABLE agents (
id uniqueidentifier PRIMARY KEY
);
INSERT INTO agents VALUES (NEWID()), (NEWID()), (NEWID()), (NEWID());
DECLARE #current_id uniqueidentifier;
DECLARE #i int = 0;
WHILE #i < 10 BEGIN
SET #current_id = COALESCE(
(SELECT TOP 1 id FROM agents WHERE #current_id < id ORDER BY id),
(SELECT TOP 1 id FROM agents ORDER BY id)
);
PRINT CONCAT('#current_id = ', #current_id);
SET #i = #i + 1;
END
You can combine two queries where the second is only run if the first one didn't return anything:
with next_agent as (
SELECT id
FROM agents
WHERE id > $1 -- the last ID retrieved or NULL if it's the first
ORDER BY id
limit 1
)
select *
from next_agent
union all
(
select *
from agents
where not exists (select * from next_agent)
order by id
limit 1
)
So if $1 is null (first call) or 'uuid4' the next_agent CTE will not return anything. And in that case the second part of the UNION in the outer query will be run picking the "first" row

SQL: Delete rows in a table where one field's value is lesser than group average

For now, I am first running the following query:
select group_name, avg(numeric_field) as avg_value, count(group_name) as n from table_name group by group_name order by n desc;
Suppose I get output:
group_name | avg_value | n
----------------------------------------
nice_group_name| 1566.353 | 2034
other_group | 235.43 | 1390
.
.
.
I am then deleting records in each group one by one manually using the following query for each group:
delete from table_name where group_name = 'nice_group_name' and numeric_field < 1567;
Here 1567 is the approximate avg_value for nice_group_name.
How can I run the second query for all rows of the result of first query automatically?
You can use a correlated subquery:
delete from table_name
where numeric_field < (select avg(t2.numeric_field)
from table_name t2
where t2.group_name = table_name.group_name
);
For performance, you want an index on tablename(group_name, numeric_field).
If you have few groups, you might find this more efficient:
with a as (
select group_name, avg(numeric_field) as anf
from table_name
group by group_name
)
delete from table_name
where numeric_field < (select a.anf from a where a.group_name = table_name.group_name);
If table_name has some primary key field (say id) then use the following:
alter table table_name rename to bak;
create temp table avg_val as
select group_name as g, avg(numeric_field) as a from bak
group by group_name;
create table table_name as
select * from bak where id in (
select bak.id from
avg_val join bak on bak.group_name = avg_val.g
where avg_val.a <= bak.numeric_field
);
Check table_name. If all has went well you can delete the backed up old table:
drop table bak;
Briefly, the steps are:
Rename the original table
Create a temporary table of average value for each group
Create a new table with all rows from original table where numeric_field is not less than average for that group.
delete the renamed original table.

Reuse of select query result oracle

I've got following query
SELECT ID FROM MARMELADES mrm
where not exists
(SELECT 1 FROM TOYS toys
WHERE mrm.ID = toys.ID
AND mrm.INGREDIENT = toys.INGREDIENT
AND mrm.BOX_TYPE = 2)
AND mrm.BOX_TYPE = 2
It returns almost 400+ results of id, for example [12, 33, 45, ... , 3405]
Now, i want to remove all ids that are from that list everywhere from my database. this is not only MARMELADES and TOYS. Also, i have for example 35+ tables where i can have this id).
I would be happy if this query could extract in some functions like ALL_UNNEEDED_IDS so i can use it like this:
DELETE FROM ANOTHER_TABLE_1 WHERE ID IN ( ALL_UNNEEDED_IDS )
DELETE FROM ANOTHER_TABLE_2 WHERE ID IN ( ALL_UNNEEDED_IDS )
DELETE FROM ANOTHER_TABLE_3 WHERE ID IN ( ALL_UNNEEDED_IDS )
DELETE FROM ANOTHER_TABLE_4 WHERE ID IN ( ALL_UNNEEDED_IDS )
...
DELETE FROM ANOTHER_TABLE_35 WHERE ID IN ( ALL_UNNEEDED_IDS )
It is possible to do it in oracle to reuse such results?
Use the first query within your subsequent queries. IE:
DELETE FROM ANOTHER_TABLE_1 WHERE ID IN (
SELECT ID FROM MARMELADES mrm
where not exists
(SELECT 1 FROM TOYS toys
WHERE mrm.ID = toys.ID
AND mrm.INGREDIENT = toys.INGREDIENT
AND mrm.BOX_TYPE = 2)
AND mrm.BOX_TYPE = 2
);
When you get to the toys and marmelades tables, you'll need a temporary holder table as #Gordon suggests.

How to lock a row so that a select returns a row exactly once?

SQL amateur here.
I have a table containing voucher codes:
id voucherCode UserId
------------------------
1 abcde 13
2 bcdef 11
3 cdefg
4 defgh
Now if one of my users qualify for a voucher code, I get one from my table:
SELECT TOP 1 * FROM tableName WHERE UserId IS NULL;
UPDATE tableName SET UserId = #UserID WHERE id = #SelectedIdFromAboveSelect;
I have to be sure that each row is only selected once. If multiple users need a voucher code, there is a small time gap between the SELECT and the UPDATE. If then a second SELECT is executed, it may SELECT the same row a second time. How do I prevent that?
I guess you can do it in opposite order; I mean, you UPDATE the table with the user ID and then SELECT the voucherCode. This way you shouldn't have any problem.
You can do this by returning the value in the update statement, using the output clause:
declare #ids table (userid int);
with toupdate as (
SELECT TOP 1 *
FROM tableName
WHERE UserId IS NULL
)
UPDATE toupdate
SET UserId = #UserId
OUTPUT inserted.UserId INTO #ids;
You can try this:
UPDATE t
SET userid = 99
OUTPUT inserted.id, inserted.vouchercode
FROM tablename t
WHERE userid IS NULL
AND id = (SELECT MIN(id)
FROM tablename
WHERE userid IS NULL)
the OUTPUT is only for getting the id and code you actually selected.
I think this may help :
SELECT DISTINCT * FROM tableName WHERE UserId IS NULL

Tricky MS Access SQL query to remove surplus duplicate records

I have an Access table of the form (I'm simplifying it a bit)
ID AutoNumber Primary Key
SchemeName Text (50)
SchemeNumber Text (15)
This contains some data eg...
ID SchemeName SchemeNumber
--------------------------------------------------------------------
714 Malcolm ABC123
80 Malcolm ABC123
96 Malcolms Scheme ABC123
101 Malcolms Scheme ABC123
98 Malcolms Scheme DEF888
654 Another Scheme BAR876
543 Whatever Scheme KJL111
etc...
Now. I want to remove duplicate names under the same SchemeNumber. But I want to leave the record which has the longest SchemeName for that scheme number. If there are duplicate records with the same longest length then I just want to leave only one, say, the lowest ID (but any one will do really). From the above example I would want to delete IDs 714, 80 and 101 (to leave only 96).
I thought this would be relatively easy to achieve but it's turning into a bit of a nightmare! Thanks for any suggestions. I know I could loop it programatically but I'd rather have a single DELETE query.
See if this query returns the rows you want to keep:
SELECT r.SchemeNumber, r.SchemeName, Min(r.ID) AS MinOfID
FROM
(SELECT
SchemeNumber,
SchemeName,
Len(SchemeName) AS name_length,
ID
FROM tblSchemes
) AS r
INNER JOIN
(SELECT
SchemeNumber,
Max(Len(SchemeName)) AS name_length
FROM tblSchemes
GROUP BY SchemeNumber
) AS w
ON
(r.SchemeNumber = w.SchemeNumber)
AND (r.name_length = w.name_length)
GROUP BY r.SchemeNumber, r.SchemeName
ORDER BY r.SchemeName;
If so, save it as qrySchemes2Keep. Then create a DELETE query to discard rows from tblSchemes whose ID value is not found in qrySchemes2Keep.
DELETE
FROM tblSchemes AS s
WHERE Not Exists (SELECT * FROM qrySchemes2Keep WHERE MinOfID = s.ID);
Just beware, if you later use Access' query designer to make changes to that DELETE query, it may "helpfully" convert the SQL to something like this:
DELETE s.*, Exists (SELECT * FROM qrySchemes2Keep WHERE MinOfID = s.ID)
FROM tblSchemes AS s
WHERE (((Exists (SELECT * FROM qrySchemes2Keep WHERE MinOfID = s.ID))=False));
DELETE FROM Table t1
WHERE EXISTS (SELECT 1 from Table t2
WHERE t1.SchemeNumber = t2.SchemeNumber
AND Length(t2.SchemeName) > Length(t1.SchemeName)
)
Depend on your RDBMS you may use function different from Length (Oracle - length, mysql - length, sql server - LEN)
delete ShortScheme
from Scheme ShortScheme
join Scheme LongScheme
on ShortScheme.SchemeNumber = LongScheme.SchemeNumber
and (len(ShortScheme.SchemeName) < len(LongScheme.SchemeName) or (len(ShortScheme.SchemeName) = len(LongScheme.SchemeName) and ShortScheme.ID > LongScheme.ID))
(SQL Server flavored)
Now updated to include the specified tie resolution. Although, you may get better performance doing it in two queries: first deleting the schemes with shorter names as in my original query and then going back and deleting the higher ID where there was a tie in name length.
I'd do this in multiple steps. Large delete operations done in a single step make me too nervous -- what if you make a mistake? There's no sql 'undo' statement.
-- Setup the data
DROP Table foo;
DROP Table bar;
DROP Table bat;
DROP Table baz;
CREATE TABLE foo (
id int(11) NOT NULL,
SchemeName varchar(50),
SchemeNumber varchar(15),
PRIMARY KEY (id)
);
insert into foo values (714, 'Malcolm', 'ABC123' );
insert into foo values (80, 'Malcolm', 'ABC123' );
insert into foo values (96, 'Malcolms Scheme', 'ABC123' );
insert into foo values (101, 'Malcolms Scheme', 'ABC123' );
insert into foo values (98, 'Malcolms Scheme', 'DEF888' );
insert into foo values (654, 'Another Scheme ', 'BAR876' );
insert into foo values (543, 'Whatever Scheme ', 'KJL111' );
-- Find all the records that have dups, find the longest one
create table bar as
select max(length(SchemeName)) as max_length, SchemeNumber
from foo
group by SchemeNumber
having count(*) > 1;
-- Find the one we want to keep
create table bat as
select min(a.id) as id, a.SchemeNumber
from foo a join bar b on a.SchemeNumber = b.SchemeNumber
and length(a.SchemeName) = b.max_length
group by SchemeNumber;
-- Select into this table all the rows to delete
create table baz as
select a.id from foo a join bat b where a.SchemeNumber = b.SchemeNumber
and a.id != b.id;
This will give you a new table with only records for rows that you want to remove.
Now check these out and make sure that they contain only the rows you want deleted. This way you can make sure that when you do the delete, you know exactly what to expect. It should also be pretty fast.
Then when you're ready, use this command to delete the rows using this command.
delete from foo where id in (select id from baz);
This seems like more work because of the different tables, but it's safer probably just as fast as the other ways. Plus you can stop at any step and make sure the data is what you want before you do any actual deletes.
If your platform supports ranking functions and common table expressions:
with cte as (
select row_number()
over (partition by SchemeNumber order by len(SchemeName) desc) as rn
from Table)
delete from cte where rn > 1;
try this:
Select * From Table t
Where Len(SchemeName) <
(Select Max(Len(Schemename))
From Table
Where SchemeNumber = t.SchemeNumber )
And Id >
(Select Min (Id)
From Table
Where SchemeNumber = t.SchemeNumber
And SchemeName = t.SchemeName)
or this:,...
Select * From Table t
Where Id >
(Select Min(Id) From Table
Where SchemeNumber = t.SchemeNumber
And Len(SchemeName) <
(Select Max(Len(Schemename))
From Table
Where SchemeNumber = t.SchemeNumber))
if either of these selects the records that should be deleted, just change it to a delete
Delete
From Table t
Where Len(SchemeName) <
(Select Max(Len(Schemename))
From Table
Where SchemeNumber = t.SchemeNumber )
And Id >
(Select Min (Id)
From Table
Where SchemeNumber = t.SchemeNumber
And SchemeName = t.SchemeName)
or using the second construction:
Delete From Table t Where Id >
(Select Min(Id) From Table
Where SchemeNumber = t.SchemeNumber
And Len(SchemeName) <
(Select Max(Len(Schemename))
From Table
Where SchemeNumber = t.SchemeNumber))