combine multiple rows from the same table (days of the week) - mysql

I have a table which stores yes/no values for each hour of the day for a particular user/filter/type. Each day is its own row. So there will always be 7 rows for any given user/filter/type combination.
What I am trying to accomplish is one result for each user/filter/type combination that contains all hours of each day of the week. I think the approach I need here is self joining with aliases and groups, but everything I have tried fails. I have setup a basic fiddle at fiddle
I also have the ability to change the db schema for this as well if there is an easier and/or preferred method to handle this on the db side (my gut says there is). Perhaps a table for each day of the week linked by filter_id?
INSERT INTO filters
(`filter_id`, `user_id`, `filter`, `type`, `day`, `12a`, `1a`... and so on for each hour)
VALUES
(1, 1, 'filter1', 1, 1, 1, 1),
(2, 1, 'filter1', 1, 2, 1, 1),
(3, 1, 'filter1', 1, 3, 1, 1),
(4, 1, 'filter1', 1, 4, 1, 1),
(5, 1, 'filter1', 1, 5, 1, 1),
(6, 1, 'filter1', 1, 6, 1, 1),
(7, 1, 'filter1', 1, 7, 1, 1),
(8, 1, 'filter2', 1, 1, 0, 0),
(9, 1, 'filter2', 1, 2, 0, 0),
(10, 1, 'filter2', 1, 3, 0, 0),
(11, 1, 'filter2', 1, 4, 0, 0),
(12, 1, 'filter2', 1, 5, 0, 0),
(13, 1, 'filter2', 1, 6, 0, 0),
(14, 1, 'filter2', 1, 7, 0, 0),
(15, 1, 'filter3', 1, 1, 0, 0),
(16, 1, 'filter3', 1, 2, 0, 0),
(17, 1, 'filter3', 1, 3, 0, 0),
(18, 1, 'filter3', 1, 4, 0, 0),
(19, 1, 'filter3', 1, 5, 0, 0),
(20, 1, 'filter3', 1, 6, 0, 0),
(21, 1, 'filter3', 1, 7, 0, 0)
;
EDIT :
I made some progress on this and it is showing all day hours for the filter in one result...however... I it does not work when a user has more than one filter. I can't seem to get the grouping correct and/or something else so results only show unique user/type/filter combinations... at the moment it only shows one filter result for each user.
This is only joining monday, tuesday, wednesday as well... there must be an easier way to do this. Like I said I am totally open up to changing the schema of the db for this as well, but not sure what the best approach would be other than this. I certainly cannot list all the hours for the entire week in one table (that would be 168 columns for hours plus an additional for for 172 in each row).
$stmt = $db->prepare("
SELECT users.user_id, users.username, c.computer_name, filters.user_id, filters.filter, filters.type,
monday.12a as m12a,
monday.1a as m1a,
monday.2a as m2a,
monday.3a as m3a,
monday.4a as m4a,
monday.5a as m5a,
monday.6a as m6a,
monday.7a as m7a,
monday.8a as m8a,
monday.9a as m9a,
monday.10a as m10a,
monday.11a as m11a,
monday.12p as m12p,
monday.1p as m1p,
monday.2p as m2p,
monday.3p as m3p,
monday.4p as m4p,
monday.5p as m5p,
monday.6p as m6p,
monday.7p as m8p,
monday.9p as m9p,
monday.10p as m10p,
monday.11p as m11p,
tuesday.12a as t12a,
tuesday.1a as t1a,
tuesday.2a as t2a,
tuesday.3a as t3a,
tuesday.4a as t4a,
tuesday.5a as t5a,
tuesday.6a as t6a,
tuesday.7a as t7a,
tuesday.8a as t8a,
tuesday.9a as t9a,
tuesday.10a as t10a,
tuesday.11a as t11a,
tuesday.12p as t12p,
tuesday.1p as t1p,
tuesday.2p as t2p,
tuesday.3p as t3p,
tuesday.4p as t4p,
tuesday.5p as t5p,
tuesday.6p as t6p,
tuesday.7p as t8p,
tuesday.9p as t9p,
tuesday.10p as t10p,
tuesday.11p as t11p,
wednesday.12a as w12a,
wednesday.1a as w1a,
wednesday.2a as w2a,
wednesday.3a as w3a,
wednesday.4a as w4a,
wednesday.5a as w5a,
wednesday.6a as w6a,
wednesday.7a as w7a,
wednesday.8a as w8a,
wednesday.9a as w9a,
wednesday.10a as w10a,
wednesday.11a as w11a,
wednesday.12p as w12p,
wednesday.1p as w1p,
wednesday.2p as w2p,
wednesday.3p as w3p,
wednesday.4p as w4p,
wednesday.5p as w5p,
wednesday.6p as w6p,
wednesday.7p as w8p,
wednesday.9p as w9p,
wednesday.10p as w10p,
wednesday.11p as w11p
FROM
( SELECT account_id, computer_id, computer_name
FROM computers
WHERE account_id = 1
ORDER BY computer_id ASC LIMIT 0, 5
) as c
LEFT JOIN users
on users.computer_id = c.computer_id
LEFT JOIN filters
on filters.user_id = users.user_id
LEFT JOIN filters as monday
on monday.user_id = filters.user_id and monday.filter = filters.filter and monday.day = 1
LEFT JOIN filters as tuesday
on tuesday.user_id = filters.user_id and tuesday.filter = filters.filter and tuesday.day = 2
LEFT JOIN filters as wednesday
on wednesday.user_id = filters.user_id and wednesday.filter = filters.filter and wednesday.day = 3
WHERE filters.type = 1
GROUP BY users.user_id
");

The INNER JOIN keyword selects all rows from both tables as long as there is a match between the columns in both tables.
SELECT column_name(s)
FROM table1
INNER JOIN table2
ON table1.column_name=table2.column_name;
Here is the official mysql page. http://dev.mysql.com/doc/refman/5.0/en/join.html

Related

SQL query INSERT column count error

I have removed 'age_range' from this query.
INSERT INTO `filters` (`id`, `user_id`, `profession_preference`, `country`, `city`, `order_by`, `profession_orientation`, `age_range`, `distance_range`, `location_dating`) VALUES
(9, 20, 3, 'All Countries', '', 2, 1, '16,100', '0,500', 0),
(10, 12, 3, 'Egypt', '', 2, 1, '', '', 0),
(11, 19, 3, 'All Countries', '', 2, 1, '16,100', '0,500', 0),
(13, 20, 3, 'All Countries', '', 2, 1, '16,100', '0,500', 0),
(14, 20, 3, 'All Countries', '', 2, 1, '16,100', '0,500', 0),
(15, 20, 3, 'All Countries', '', 2, 1, '16,100', '0,500', 0),
(25, 121, 3, 'All Countries', '', 3, 1, '18,23', '0,500 ', 0),
(26, 316, 3, 'United States', '', 3, 1, '17,25', '0,500', 0);
I executed again and receive this error:
MySQL said: Documentation
#1136 - Column count doesn't match value count at row 1
When you insert a record, it matches the values in the VALUES list to the columns in the columns list by comma-separated position. So, this insert statement:
INSERT INTO `foo` (`A`, `B`, `C`)
VALUES ('valueA', 'valueB', 'valueC')
It will insert valueA into column A, valueB into column B, etc. because they match positions in their respective lists. If you remove B from the columns list and leave VALUES alone, it will not attempt to insert valueA into column A, but valueB into column C because they now match value positions, but it won't know what to do with valueC because there are now more values than columns, so since you removed the column from the second position, you would also need to remove the value from the second position.
So back to your query, you would need to determine which position age_range occupied in the columns list and remove the values from the same position in the values list.
Does that make sense?
You have 9 columns defined in your insert statement and you are trying to insert 10 values. you either need to add another column definition or remove from your values.
According to rule the column name define and provided values count should be same. In your case one column value in extra.
As the documentation says
"Column count doesn't match value count"
You specify 9 columns (id, user_id, profession_preference, country, city, order_by, profession_orientation, distance_range, location_dating) in your insert statement
and you are trying to insert 10 values.
You have to remove one value or add another column
Before removing column this is the script which will work
CREATE TABLE filters (id INT, user_id INT, profession_preference INT, country VARCHAR(50), city VARCHAR(50), order_by INT, profession_orientation INT, age_range VARCHAR(50), distance_range VARCHAR(50), location_dating INT);
INSERT INTO filters (id, user_id, profession_preference, country, city, order_by, profession_orientation, age_range, distance_range, location_dating) VALUES
(9, 20, 3, 'All Countries', '', 2, 1, '16,100', '0,500', 0),
(10, 12, 3, 'Egypt', '', 2, 1, '', '', 0),
(11, 19, 3, 'All Countries', '', 2, 1, '16,100', '0,500', 0),
(13, 20, 3, 'All Countries', '', 2, 1, '16,100', '0,500', 0),
(14, 20, 3, 'All Countries', '', 2, 1, '16,100', '0,500', 0),
(15, 20, 3, 'All Countries', '', 2, 1, '16,100', '0,500', 0),
(25, 121, 3, 'All Countries', '', 3, 1, '18,23', '0,500 ', 0),
(26, 316, 3, 'United States', '', 3, 1, '17,25', '0,500', 0);
Now since you removed age_range column, below script will work:
INSERT INTO filters (id, user_id, profession_preference, country, city, order_by, profession_orientation, distance_range, location_dating) VALUES
(9, 20, 3, 'All Countries', '', 2, 1, '0,500', 0),
(10, 12, 3, 'Egypt', '', 2, 1, '', 0),
(11, 19, 3, 'All Countries', '', 2, 1, '0,500', 0),
(13, 20, 3, 'All Countries', '', 2, 1, '0,500', 0),
(14, 20, 3, 'All Countries', '', 2, 1, '0,500', 0),
(15, 20, 3, 'All Countries', '', 2, 1, '0,500', 0),
(25, 121, 3, 'All Countries', '', 3, 1, '0,500', 0),
(26, 316, 3, 'United States', '', 3, 1, '0,500', 0);
I removed third last column from insert script.
Hope this helps!

Transfer mySQL data structure into new relation database (comma separated to relation table)

I have a several mySQL tables where I have saved the relation ID of the child table comma separated. Now I have to transfer this entries into a new table where for each relation is one entry.
Is there an easy way to transfer import query into the correct format?
Here the data example, my old table (cat_projects) has the following entries I want convert:
-- export of table cat_projects
INSERT INTO `cat_projects` (`id`, `authors`) VALUES
(2, '4,1'),
(3, '0'),
(4, '8,4,1'),
(5, '13,12'),
(10, '19,4,1'),
(13, ''),
(14, ''),
(15, '28,27,25,12,9,1');
This entries I want just to write into the new relation table (cat_project_relation). The att_id links to the another table where I have save the settings of the old authors column:
-- att_id = 58
-- item_id = id
-- value_sorting = counting from 0 for each item_id
-- value_id = for each relation one entry value
INSERT INTO `cat_project_relation` (`att_id`, `item_id`, `value_sorting`, `value_id`) VALUES
(58, 2, 0, '4'),
(58, 2, 1, '1'),
(58, 3, 0, '0'),
(58, 4, 0, '8'),
(58, 4, 1, '4'),
(58, 4, 2, '1'),
(58, 5, 0, '13'),
(58, 5, 1, '12'),
(58, 10, 0, '19'),
(58, 10, 1, '4'),
(58, 10, 2, '1'),
(58, 13, 0, ''),
(58, 14, 0, ''),
(58, 15, 0, '28'),
(58, 15, 1, '27'),
(58, 15, 2, '25'),
(58, 15, 3, '12'),
(58, 15, 4, '9'),
(58, 15, 5, '1');
I hope it is clear what I try to achieve. Is it possible to do that directly in SQL or do I have to apply an external bash script?
Actually it wasn't as difficult as I thought to write a little bash script. And I guess its the easier solution then program something in SQL.
#!/bin/bash
# input table
table="(2, '4,1'),
(3, '0'),
(4, '8,4,1'),
(5, '13,12'),
(10, '19,4,1'),
(13, ''),
(14, ''),
(15, '28,27,25,12,9,1');"
# fixed attribute id
att_id=58
# read each line into an array
readarray -t y <<<"$table"
# for each array item (each line)
for (( i=0; i<${#y[*]}; i=i+1 ))
do
z=${y[$i]}
# split by comma into array
IFS=', ' read -r -a array <<< "$z"
# loop through each value
for (( j=0; j<${#array[*]}; j=j+1 ))
do
# remove all other characters then number
nr=$(echo ${array[$j]} | sed 's/[^0-9]*//g')
# each first value is the item_id
if [ $j -eq 0 ]
then
item_id=$nr
else
k=$(expr $j - 1)
value_id=$nr
# print output line by line
echo "($att_id, $item_id, $k, '$value_id')," >> output.txt
fi
done
done
The result will be as the on asked in the question.

How to use SUM IF() in MySQL without hard coding values in the query

I have a query that uses SUM IF() to do a cross-tab result set. In this query I have the value sin the SUM IF() hard coded. The problem is that new values are added to the database. Is there a way to write the query without hard coding the values in the SUM IF()? Here is the query:
select storeid, sum(if(marketsegmentid = 6, 1, 0)) as 6,
sum(if(marketsegmentid = 7, 1, 0))
as 7, sum(if(marketsegmentid = 12, 1, 0)) as 12, sum(if(marketsegmentid = 17, 1, 0)) as 17,
sum(if(marketsegmentid = 22, 1, 0)) as 22, sum(if(marketsegmentid = 27, 1, 0)) as 27,
sum(if(marketsegmentid = 32, 1, 0)) as 32, sum(if(marketsegmentid = 37, 1, 0)) as 37,
sum(if(marketsegmentid = 42, 1, 0)) as 42, sum(if(marketsegmentid = 47, 1, 0)) as 47,
sum(if(marketsegmentid = 52, 1, 0)) as 52, sum(if(marketsegmentid = 97, 1, 0)) as 97,
sum(if(marketsegmentid = 102, 1, 0)) as 102, sum(if(marketsegmentid = 107, 1, 0)) as 107,
sum(if(marketsegmentid = 112, 1, 0)) as 112, sum(if(marketsegmentid = 117, 1, 0)) as 117,
sum(if(marketsegmentid = 122, 1, 0)) as 122, sum(if(marketsegmentid = 127, 1, 0)) as 127,
sum(if(marketsegmentid = 132, 1, 0)) as 132, sum(if(marketsegmentid = 137, 1, 0)) as 137,
sum(if(marketsegmentid = 142, 1, 0)) as 142
from storemarketsegments
group by storeid;
The query is used in a report and the results are exported to CSV. The 1's are used as flags in the result set.
The table I am querying is set up like this:
CREATE TABLE storemarketsegments(id INT NOT NULL, marketsegmentid INT NOT NULL);
The marketsegments are kept in a separate table:
CREATE TABLE marketsegment(ID INT NOT NULL AUTO_INCREMENT, PRIMARY
KEY(id), name VARCHAR(45), description VARCHAR(45));
Any help would be greatly appreciated. I am not sure if there is a way to write the query without hard coding the values and I don't mind updating the query in the report whenever new marketsegments are added but thought I would check. Thank you in advance for your assistance.
You can return each count as a separate row, and then filter as needed in the application layer:
select storeid, marketsegmentid, count(*) as Count
from storemarketsegments
group by storeid, marketsegmentid;

Add up a points column based on an id from within the same mysql table

OK the database is layed out as (only columns being used are listed):
Table Name: race_stats
Columns: race_id, user_id, points, tournament_id
Table Name: user
Columns: user_id, driver
Table Name: race
Columns: race_id, race_name
Table Name: tournament
Columns: tournament_id, tournament_name
This is my current query:
$query = "
SELECT user.user_id, user.driver, race_stats.points, race_stats.user_id,
SUM(race_stats.points) AS total_points "."
FROM user, race_stats, tournament, race "."
WHERE race.race_id=race_stats.race_id
AND user.user_id=race_stats.user_id
AND tournament.tournament_id=race_stats.tournament_id
GROUP BY driver
ORDER BY total_points DESC
LIMIT 0, 15
";
Ok the query works but it is adding them all up for all the available races from the race_stats.race_id column as the total points. I have racked my brain beyond recognition to fix this but I just can't quite seem to find the solution I need. I'm sure it has to be an easy fix but I just can't get it. Any help is greatly appreciated.
///////////////////EDITED WITH RAW VALUES//////////////////////
INSERT INTO `race_stats` (`id_race`, `race_id`, `user_id`, `f`, `s`, `race_interval`, `race_laps`, `led`, `points`, `total_points`, `race_status`, `tournament_id`, `driver`, `tournament_name`) VALUES
(1, 1, 4, 1, 4, '135.878', 60, '2', 180, 0, 'Running', 1, 'new_driver_5', ''),
(2, 1, 2, 2, 2, '-0.08', 60, '22', 175, 0, 'Running', 1, 'new_driver_38', ''),
(3, 1, 5, 3, 5, '-11.82', 60, '2', 170, 0, 'Running', 1, 'new_driver_94', ''),
(4, 2, 2, 1, 15, '138.691', 29, '6', 180, 0, 'Running', 2, 'new_driver_38', ''),
(5, 2, 15, 2, 9, '-16.12', 29, '8*', 180, 0, 'Running', 2, 'new_driver_44', ''),
(6, 2, 8, 3, 11, '-2:03.48', 29, '0', 165, 0, 'Running', 2, 'new_driver_83', ''),
Let me know if this is what you meant by raw values if not I can get some more data for you.
Just posting the solution here for completeness:
SELECT user.driver, race_stats.race_id,
SUM(race_stats.points) AS total_points "."
FROM user, race_stats "."
WHERE user.user_id=race_stats.user_id
GROUP BY user.driver, race.race_id
Here's the query you want (formatted for readability):
SELECT
u.driver,
SUM(rs.points) AS total_points
FROM user u
LEFT JOIN race_stats rs on rs.user_id = u.user_id
GROUP BY 1;
The advantage of using an outer join (ie LEFT JOIN) is that drivers who have no stats still get a row, but with null as total_points.
p.s. I don't know what the usage of "." in your query is all about, so I removed it.

what would be a good db design for database base email system?

I have a simple table like this in mind to create a db base email system,
is this the best approach ?
TABLE `message`
- id
- parent_id
- message
- subject
- created_on
- is_draft
- sender_profile_id
TABLE `email_message`
- id
- is_read
- is_deleted
- message_id
- profile_id
case 1: profile A sending email to profile B,
and B Replies back (one to one
communication)
INSERT INTO `message` (`id`, `parent_id`, `message`, `subject`, `created_on`, `is_draft`, `sender_profile_id`) VALUES
(1, 0, 'Hi what''s up how are u', 'Hi', '2010-12-08 11:27:54', 0, 1),
(2, 1, 'yeah i am gud', 0, '2010-12-08 11:28:19', 0, 2);
INSERT INTO `email_message` (`id`, `is_read`, `is_deleted`, `message_id`, `profile_id`) VALUES
(1, 1, 0, 1, 2),
(2, 1, 0, 2, 1);
case 2:
-Profile A sending email to profile B,C, D.
-Profile B repling back all to whole group.
-A replying again to whole group.
-C replies to A only
INSERT INTO `message` (`id`, `parent_id`, `message`, `subject`, `created_on`, `is_draft`, `receiver_profile_id`) VALUES
(3, 0, 'Hi what''s up how are u', 'Hi', '2010-12-08 11:27:54', 0, 1),
(4, 3, 'yeah i am gud.', 0, '2010-12-08 11:28:19', 0, 2),
(5, 3, 'why are u gud?', 0, '2010-12-08 11:28:19', 0, 1),
(6, 3, 'what?', 0, '2010-12-08 11:28:19', 0, 3);
INSERT INTO `email_message` (`id`, `is_read`, `is_deleted`, `message_id`, `profile_id`) VALUES
(3, 1, 0, 3, 2),
(4, 0, 0, 3, 3),
(5, 0, 0, 3, 4),
(6, 0, 0, 4, 1),
(7, 0, 0, 4, 3),
(8, 0, 0, 4, 4),
(3, 0, 0, 5, 2),
(4, 0, 0, 5, 3),
(5, 0, 0, 5, 4),
(6, 0, 0, 6, 1);
I feel like a single table would be a lot easier to impliment :
TABLE Message
- MessageId (GUID)
- ParentId (GUID)
- Subject
- Message
- To
- Sender
- CreatedOn
- isDraft
- isDeleted
- isRead
If you are sending an email to a group, just create multiple records with different "To" entries