i have an existing table
id, name, user
15, bob, 1
25, alice, 2
30, ann, 1
55, bob, 2
66, candy, 1
we want the name records for user 1 to now be set to the values in this string:
"ann, candy, dave"
if I do it the easy way
delete from table where user = 1
insert into table (name,user) values (ann,1), (candy,1), (dave,1)`
then the table now looks like this
id, name, user
25, alice, 2
55, bob, 2
67, ann, 1
68, candy, 1
69, dave, 1
i.e. new rows are created. I don't want the new identities, and over time in huge tables, this causes fragmentation and identity holes and so on. what is the most efficient way in SQL to reduce this to just the actual 2 required operations:
delete from table where user = 1 and name is not in the string "ann, candy, dave", so that the table is then:
25, alice, 2
30, ann, 1
55, bob, 2
66, candy, 1
`
insert into table user = 1, name = any value from "ann, candy, dave" that does not match name/user=1 , so that the table is then:
25, alice, 2
30, ann, 1
55, bob, 2
66, candy, 1
67, dave, 1
It sounds like you have a list and want to process it twice, once for deletes and once for inserts. Store the list in a temporary table and use that for processing.
Along the way, start with a unique index on user, name to prevent updates into the table:
create unique index idx_table_user_name on table(user, name);
This seems to be a requirement for your data, so let the database enforce it. Then the code for processing is like:
create temporary table toprocess as (
select 1 as user, 'ann' as name union all
select 1, 'candy' union all
select 1, 'dave'
);
create index idx_toprocess_user_name on toprocess(user, name);
delete t
from table t
where t.user in (select p.user from toprocess p) and
not exists (select 1 from toprocess p where p.user = t.user and p.name = t.name);
insert into table(user, name)
select user, name
from toprocess
on duplicate key update user = values(user);
Although this might look a bit complicated, it lets you handle multiple users at the same time. And, the list for processing is only entered once, which reduces the scope for error.
It is not so clear but may be this is what you want:
delete from table where user = 1 and name not in('ann', 'candy', 'dave')
insert into table
select * from(select 'ann' as name
union all
select 'candy'
union all
select 'dave') t
where t.name not in(select name from table where user = 1)
Related
I use mysql to deal some data use multi-thread, first I search the data use id range, like:
select id
from xxx
where id between 1 and 1000
and accountant_time = '2021-05-31 00:00:00'
and enter_accounts_state = 1
and enter_ce_state = 2
and ebs_summary_state = 2
and is_del = 0
result id like '1, 2, 3, 4, 5, ... 1001'.
and second I will delete these match data with addtional condition confirm_state from table like sql below:
DELETE
FROM xxx
WHERE confirm_state = 3
AND id IN ( 1, 2, 3, 4, ..., 1001);
All of the id range no intersection。
I found that some thread need return 1001 rows, but only returned the first row,
I tried several times use same code and same data, but the left data also not same, the common feature is only return first row of that batch count which need return all。
When I add for update for the select sql, it works normal,
How can I understand what happens?
I need help for designing an SQL statement. It will be created on the fly in a Web Server back end, depending on the request. This will filter a database for the desired results.
To describe my need in simplest form:
One table, two fields, 50 staff, 30 skills.
Example:
staff_id, skill_id
1, 1
1, 2
1, 3
1, 4
2, 2
2, 3
2, 4
3, 2
4, 3
Q: Who has Skill 3
A: Staff 1,2,4
(This is easy)
Q: Who has Skill 2 AND 3
A: Staff 1,2
The actual query will have a request for 5 or 6 Skills
Considering that the number of skills varies from time to time, the best way to handle this via temp table..Here is the pseudocode...syntax needs to be fixed.
create procedure my_procedure(string of skillsets)
begin
create table #tmp_skillsets(skillset smallint)
foreach skillset in skillsets
insert into #tmp_skillsets(convert_to_smallint(skillset))
select distinct staff from myTable, #tmp_skillset
where myTable.skillset = #tmp_skillsets
end
Call this procedure like: "exec my_procedure("1456") or "exec my_procedure("179248503")
I think this should work:
SELECT DISTINCT staff_id
FROM oneTable t
WHERE EXISTS (SELECT 1 FROM oneTable ti WHERE t.staff_id = ti.staff_id AND ti.skill_id = 2)
AND EXISTS (SELECT 1 FROM oneTable ti WHERE t.staff_id = ti.staff_id AND ti.skill_id = 3)
Let's say I have the following table of user properties:
id, user_id properties
1, NULL, prop_ss1
2, NULL, prop_ss2
3, 2, prop_1
4, 2, prop_2
5, 3, prop_1
6, 3, prop_2
7, 3, prop_3
8, 4, prop_1
Given an array of user_ids, how could I get the list of all properties which either have the user_id NULL (call it a global property if you wish), or are shared among all user_ids in the given array?
For instance, given an array (2,3), I would like to get:
prop_ss1
prop_ss2
prop_1
prop_2
Or, given an array(2,3,4), I would like to get:
prop_ss1
prop_ss2
prop_1
Try a UNION of two separate queries:
SELECT properties FROM your_table WHERE user_id IS NULL
UNION
SELECT properties
FROM your_table
WHERE user_id IN (2, 3)
GROUP BY properties
HAVING COUNT(DISTINCT user_id) = 2
See it working online: sqlfiddle
The number 2 in the last line is the number of users that you are querying for.
select distinct properties from table
where user_id is null
or user_id in (1,2,3)
Sorry misread your post, need group by and having
So I have a weird scenario where I want the following data to be ordered in a certain way. Let the table data be:
abc 111 2 priority
abc 111 blah data
abc 222 1 priority
abc 222 blah data
abc 333 3 priority
abc 333 blah data
I want to order that data based on column three (where column 4 is priority) but keep the return order grouped by column 2. So the expected query result would look like:
abc 222 1 priority
abc 222 blah data
abc 111 2 priority
abc 111 blah data
abc 333 3 priority
abc 333 blah data
What's the best possible way of doing this. I can think of doing a query up front and an in clause, but then I would have to account for all possible priorities.
Thanks in advance. FYI, its MySQL that I am using.
I think this is what you need:
http://sqlfiddle.com/#!2/48057/14
DDL
CREATE TABLE my_data(
name VARCHAR(20),
num NUMERIC(5),
c NUMERIC(3),
priority NUMERIC(3)
);
DML
INSERT INTO my_data VALUES("abc", 111, 2, 1);
INSERT INTO my_data VALUES("abc", 222, 3, 4);
INSERT INTO my_data VALUES("abc", 222, 1, 9);
INSERT INTO my_data VALUES("abc", 111, 4, 2);
It would be better if you could have actually include actual column names even with bogus data, however, I've named them corresponding with the type of content I think you are presenting.
It appears your second column is some "ID" column and want to keep them all grouped together. So, pre-query that in the order you want but only for the "Priority" column ignoring the rest of the records. THEN, using MySQL variables, you can assign it a sequential value for final output. Then join to the regular data on the "ID" column for all other values... Something like...
select
md2.ABCColumn,
SortSeqQuery.IDColumn,
md2.DescripColumn,
md2.ColumnWith123Sequence
from
my_Data md2
LEFT JOIN ( select
md1.IDColumn,
#SeqVal := #SeqVal +1 as FinalSortOrder
from
my_Data md1,
( select #SeqVal := 0 ) sqlvars
where
md1.DescripColumn = "priority"
order by
md1.ColumnWith123Sequence,
md1.IDColumn ) SortSeqQuery
on md2.IDColumn = SortSeqQuery.IDColumn
order by
case when SortSeqQuery.IDColumn is null then 2 else 1 end,
coalesce( SortSeqQuery.FinalSortOrder, 0 ) as FinalSort
With the "Order By", it pre-sorts the qualified data BEFORE it actually applies the #SeqVal which in turn should come back as 1, 2, 3, 4, etc...
The "SortSeqQuery" will be run first to pre-qualify any "priority" records and have them sorted and available. Then, your "my_data" table is basis of the query and joins to that result set and grabs the appropriate sort sequence based on matching ID column. If there are IDColumn entries that DO NOT have a priority, then they will also be included, but with the order by based on a case/when, any that are null are pre-sorted to the bottom, those found will be sorted to the top. Within that sort, it will keep all IDColumn entries grouped together since the same "FinalSortOrder" is directly associated to the IDColumn in the preliminary query.
ANSWER -
CREATE TABLE my_data( name VARCHAR(20),groupid INT(5), attributekey VARCHAR(10),attributevalue VARCHAR(10));
INSERT INTO my_data VALUES("abc", 111, "priority", "2");
INSERT INTO my_data VALUES("abc", 111, "someattr", "blah");
INSERT INTO my_data VALUES("abc", 222, "priority", "1");
INSERT INTO my_data VALUES("abc", 222, "someattr", "blah");
INSERT INTO my_data VALUES("abc", 333, "priority", "3");
INSERT INTO my_data VALUES("abc", 333, "someattr", "blah");
solution -
SELECT m1.*,
(select attributevalue from my_data m2
where m1.groupid=m2.groupid
AND m2.attributekey='priority'
) groupidpriority
FROM my_data m1
order by groupidpriority;
Please help me with a mysql query, using PHP to access databse and process data:
I have variables
1) $content_number_of_characters
2) $number_of_comments
and mysql table name: udjacomments
fields of table udjacomments: id, full_name, content, comment_url, is_published, and time_added
I would like to retrieve each entry into an array. For content, I would like to only get the content up to the character $content_number_of_characters (In other words, if $content_number_of_characters is 120, and content is 300 characters, I only want the first 120 characters).
(I do not know how I could use the mysql LEFT(str, len) into the query)
Then, I only want number of records ( $number_of_comments ) in a descending order from id and if field is_published == 1
Example:
I have records with id: 50, 51, 52, 53, 54, 55, 56
all records have is_published == 1, with the exception of record with id 55; record id 55 is_published == 0. Last, variable $number_of_comments == 3
the query would retrieve records 56, 54, and 53
Thank you,
Number of comments:
$query = "SELECT * FROM udjacomments
where is_published = 1 order by id desc limit $number_of_comments"
120 chars
$query = "select id,
if(CHAR_LENGTH(content) > $content_number_of_characters, SUBSTR(content, 1, $content_number_of_characters), content)
FROM udjacomments";