I have a table of hashtags that looks something like this:
TABLE: hashtags
hashtag_id hashtag_name - UNIQUE
1 apples
2 oranges
3 life
And a table of topics that have had these hashtags tagged to them like so:
TABLE: topic_hashtags, unique index on hashtag_id and topic_id
topic_hashtag_id hashtag_id topic_id
1 1 2
2 3 18
3 3 30
4 2 15
I've been trying to make a MYSQL statement (to no success) that can do the following in pseudocode:
INSERT INTO topic_hashtags (hashtag_id, topic_id)
VALUES(SELECT LAST_INSERT_ID(INSERT INTO hashtags (hashtag_name) VALUES('somehashtag')
ON DUPLICATE KEY SELECT hashtag_id FROM hashtags WHERE hashtag_name = 'somehashtag'), 'x');
Basically I need to get the ID of the hashtag name, if it does not exist, then insert it and get the last inserted id, then use these values to record it into the hashtag_topics table. The thing is, ON DUPLICATE KEY doesn't allow you to select a column instead. Is there another way I can do this, or is it just faster to use multiple queries? (I feel like it's not though). I really don't want to have to do:
SELECT id from hashtags
if mysqli_num_rows($result) == 1):
use the id and insert
endif;
else
insert
mysqli_insert_id
use that id and insert
You can't do all of that in one query. Just use multiple queries. It will not create excessive load because modern databases and file systems are excellent at caching.
Related
I have a user table which contain a membergroupids, and user table looks like this:
userid membergroupids
1 1,2
2 2,3
3 2,3,4
and I want to use sql to output a result like this
membergroupid count
1 1
2 3
3 2
4 1
I tried use SELECT membergroupids FROM user, then use php to loop through the result and get the count, but it works with small set of user table, but I have a really big user table, the select query itself will take more than 1min to finish, is there better way to do this?
There is a much better way to do it. Your tables need to be normalized:
Instead of
userid membergroupids
1 1,2
2 2,3
3 2,3,4
It needs to be
userid membergroupids
1 1
1 2
2 2
2 3
3 2
3 3
3 4
From here, it's a simple query to get the counts (assuming this table is called your_table:
select count(membergroupids) as numberofgroups, userid
from your_table
group by userid
order by userid
The real problem, then, is getting your tables normalized. If you only have 9 membergroupids, then you could use a like '%1%' to find all userids with membergroupid #1. But if you have 10, then it won't be able to distinguish between 1 and 10. And sadly, you can't count on the commas to help you distinguish because the number might not be surrounded by commas.
unless...
Create new field with group ids encapsulated by commas
you could create a new field and populate it with membergroupids and surround it with commas by using concat (check your database's docs). Something along this line:
update your_table set temp=concat(',', membergroupids, ',');
This could give you a table structure like so:
userid membergroupids temp
1 1,2 ,1,2,
2 2,3 ,2,3,
3 2,3,4 ,2,3,4,
Now, you have the ability to grab distinct member group ids in the new field, ie, where temp like '%,1,%' to find userids with membergroupid 1. (They will be encapsulated by commas) Now, you can manually build your new normalized table which I'll call user_member.
Insert membergroupid 1:
insert into user_member (userid,membergroupid) select userid,'1' from your_table where temp like '%,1,%';
You could make a php script that loops through all the membergroupids.
Keep in mind that like %...% is not very efficient, so don't even think about relying on this to do your count. It'll work, but it's not scalable. It would be much better to use this to build the normalized table.
It's easy to do your purpose IF the data structure is as like as below:
SELECT `membergroupids`, COUNT(`membergroupids`) as
CountOfMembergroupids FROM `TBL_TEST01` WHERE 1
GROUP BY `membergroupids`
ORDER BY `userid`
As you mentioned that you have to proceed with large amount of data..., I'd strongly suggest that you could revise your table structure as above...
I need help for this problem.
In MYSQL Table i have a field :
Field : artist_list
Values : 1,5,3,401
I need to find all records for artist uid 401
I do this
SELECT uid FROM tbl WHERE artist_list IN ('401');
I have all record where artist_list fields values are '401' only, but if i have 11,401 this query do not match.
Any idea ?
(I cant user LIKE method because if artist uid is 3 (match for 30, 33, 3333)...
Short Term Solution
Use the FIND_IN_SET function:
SELECT uid
FROM tbl
WHERE FIND_IN_SET('401', artist_list) > 0
Long Term Solution
Normalize your data - this appears to be a many-to-many relationship already involving two tables. The comma separated list needs to be turned into a table of it's own:
ARTIST_LIST
artist_id (primary key, foreign key to ARTIST)
uid (primary key, foreign key to TBL)
Your database organization is a problem; you need to normalize it. Rather than having one row with a comma-separated list of values, you should do one value per row:
uid artist
1 401
1 11
1 5
2 5
2 4
2 2
Then you can query:
SELECT uid
FROM table
WHERE artist = 401
You should also look into database normalization because what you have is just going to cause more and more problems in the future.
SELECT uid
FROM tbl
WHERE CONCAT(',', artist_list, ',') LIKE '%,401,%'
Although it would make more sense to normalise your data properly in the first place. Then your query would become trivial and have much better performance.
This is an example of my table:
|..id.|.class...|.group....|..name....|
|..5..|....1....|.....A....|....XX....|
|.19..|....1....|.....B....|....XX....|
|.12..|....2....|.....A....|....XX....|
|.28..|....2....|.....B....|....XX....|
|..8..|....3....|.....A....|....XX....|
|.50..|....3....|.....B....|....XX....|
It has about 30 rows per class and group. What I'm trying to do is to fetch all data after the row | 12 | 2 | A | XX |. Can't just state "where class > 2" since there are still some rows with class and group 2A that I need to be in the select.
Is there a way to do that, from SELECT or maybe a Fetch() argument in PHP & Mysql
Thanks!
Try this:
SELECT * FROM `table`
WHERE
CONCAT(`CLASS`, `GROUP`, `NAME`) >= '2AMarcus'
Select all ids and loop through them creating a comma-delimited list in PHP of the ids after 12 is found. Then do your select where id in ().
Or
Create the list of ids to exclude until 12 is found. Then do select where id not in ().
It looks like you need some work on normalizing tables, out of sql sentences.
If you need the rows after Class 2 Group A Name Marcus, it says to me that something occur in real life from that point in the time, an event, so, i would add a new column for timestamp or for another data for that event, and then back to sql sentences and use that new column for the apropiate SELECT / WHERE.
I am quite new to PHP and MySQL, but have experience of VBA and C++. In short, I am trying to count the occurrences of a value (text string), which can appear in 11 columns in my table.
I think I will need to populate a single-dimensional array from this table, but the table has 14 columns (named 'player1' to 'player14'). I want each of these 'players' to be entered into the one-dimensional array (if not NULL), before proceeding to the next row.
I know there is the SELECT DISTINCT statement in MySQL, but can I use this to count distinct occurrences across 14 columns?
For background, I am building a football results database, where player1 to player14 are the starting 11 (and 3 subs), and my PHP code will count the number of times a player has made an appearance.
Thanks for all your help!
Matt.
Rethink your database schema. Try this:
Table players:
player_id
name
Table games:
game_id
Table appearances:
appearance_id
player_id
game_id
This reduces the amount of duplicate data. Read up on normalization. It allows you to do a simple select count(*) from appearances inner join players on player_id where name='Joe Schmoe'
First of all, the database schema you're using is terrible, and you just found out a reason why.
That being said, I see no other way then to first get a list of all players by distinctly selecting the names of players into an array. Before each insertion, you would have to check if the name is already in the array (if it is already in, don't add it again).
Then, when you have the list of names, you would have to run an SQL statement for each player, adding up the number of occurences, like so:
SELECT COUNT(*)
FROM <Table>
WHERE player1=? OR player2=? OR player3=? OR ... OR player14 = ?
That is all pretty complicated, and as I said, you should really change your database schema.
This sounds like a job for fetch_assoc (http://php.net/manual/de/mysqli-result.fetch-assoc.php).
If you use mysqli, you would get each row as an associative array.
On the other hand the table design seems a bit flawed, as suggested before.
If you had on table team with team name and what not and one table player with player names.
TEAM
| id | name | founded | foo |
PLAYER
| id | team_id | name | bar |
With that structure you could add 14 players, which point at the same team and by joining the two tables, extract the players that match your search.
I have a table which gets entries from a website, and as those entries go into the database, they need to be assigned the next category on a list of categories that may be changed at any time.
Because of this reason I can't do something simple like for mapping the first category of 5 to IDs 1, 6, 11, 16.
I've considered reading in the list of currently possibly categories, and checking the value of the last one inserted, and then giving the new record the next category, but I imagine if two requests come in at the same moment, I could potentially assign them both the same category rather then in sequence.
So, my current round of thinking is the following:
lock the tables ( categories and records )
insert the newest row into records
get the newest row's ID
select the row previous to the insertl ( by using order by auto_inc_name desc 0, 1 )
take the previous row's category, and grab the next one from the cat list
update the new inserted row
unlock the table
I'm not 100% sure this will work right, and there's possibly a much easier way to do it, so I'm asking:
A. Will this work as I described in the original problem?
B. Do you have a better/easier way to do this?
Thanks ~
I would do it way simpler... just make a table with one entry, "last_category" (unsigned tinyint not_null). Every time you do an insert just increment that value, and reset as necessary.
I'm not sure I understand your problem, but as I understand it you would like to have something like
category | data
-----------------
0 | lorem
1 | ipsum
.... | ...
4 | dolor
0 | sit
... | ...
How about having a unique auto_increment column, and let category be the MOD 5 of this column?
If you need 100% correct behaviour it sounds like you will need to lock something somewhere so that all your inserts line up properly. You might be able to avoid locking the category table if you use a single SQL statement to insert your data. I'm not sure how MySQL differs but in Oracle I can do this:
insert into my_table (id, col1, col2, category_id)
select :1, :2, :3, :4, c.id -- :1, :2, etc are bind variables. :1 corresponds to the ID.
from
(select
id, -- category id
count(*) over (partition by 1) cnt, -- count of how many categories there are
row_number() over (partition by 1 order by category.id) rn -- row number for current row in result set
from category) c
where c.rn = mod(:1, cnt)
This way in one statement I insert the next record based on the categories that existed at that moment. The insert automatically locks the my_table table until you commit. It grabs the category based on the modulus of the ID. This link shows you how to do a row-number in mysql. I'm not sure if count(*) requires group by in mysql; in oracle it does so I used a partition instead to count the whole result set.