I have 2 tables
account
id | desc
Prices
id | desc
Table account stores users info while table prices stores several prices depending type of service.
Now, I need to assign prices that apply to each account.
I would like to display a result containg prices and an extra column that tells (in form of a list) the accounts that apply that service...
I was thinking on
CREATE TABLE `account` (
`id_account` smallint(2) unsigned PRIMARY KEY AUTO_INCREMENT,
`user` VARCHAR(55) ,
`pass` VARCHAR(55) ,
`descr` VARCHAR(250)
);
INSERT INTO account VALUES
(1,'67395' , 'pass1','DrHeL'),
(2,'12316' , 'pass2','DeHrL'),
(3,'92316' , 'pass3','EfL');
CREATE TABLE `prices`(
`id_price` smallint(2) unsigned PRIMARY KEY AUTO_INCREMENT,
`service` VARCHAR(40),
`cost_1_1Kg` double ,
`cost_4_1Kg` double ,
`cost_8_1Kg` double
);
INSERT INTO prices VALUES
(1,'laundry', 1.50, 2.00,5.00),
(2,'walk.' , 2.50, 3.00,4.00);
CREATE TABLE `account_prices` (
`id_account` smallint(2) unsigned NOT NULL,
`id_price` smallint(2) unsigned NOT NULL,
`descr` VARCHAR(250)
) ;
INSERT INTO account_prices VALUES
(1,1,'apply SERVICE WITH ID 1 AND SERVICE WITH ID 2'),
(2,1,'apply SERVICE WITH ID 1 AND SERVICE WITH ID 2'),
(3,1,'apply SERVICE WITH ID 1 AND SERVICE WITH ID 2'),
(1,2,'apply SERVICE WITH ID 1 AND SERVICE WITH ID 2'),
(2,2,'apply SERVICE WITH ID 1 AND SERVICE WITH ID 2'),
(3,2,'apply SERVICE WITH ID 1 AND SERVICE WITH ID 2');
This gives me
ID_ACCOUNT ID_PRICE DESCR USER PASS SERVICE COST_1_1KG COST_4_1KG COST_8_1KG
1 1 apply SERVICE WITH ID 1 AND SERVICE WITH ID 2 67395 pass1 laundry 1.5 2 5
2 1 apply SERVICE WITH ID 1 AND SERVICE WITH ID 2 12316 pass2 laundry 1.5 2 5
3 1 apply SERVICE WITH ID 1 AND SERVICE WITH ID 2 92316 pass3 laundry 1.5 2 5
1 2 apply SERVICE WITH ID 1 AND SERVICE WITH ID 2 67395 pass1 walk. 2.5 3 4
2 2 apply SERVICE WITH ID 1 AND SERVICE WITH ID 2 12316 pass2 walk. 2.5 3 4
3 2 apply SERVICE WITH ID 1 AND SERVICE WITH ID 2 92316 pass3 walk. 2.5 3 4
However I would like somethink like:
ID_PRICE SERVICE COST_1_1KG COST_4_1KG COST_8_1KG descr
1 laundry 1.5 2 5 acount 1, account 2, account 3
2 walk. 2.5 3 4 acount 1, account 2, account 3
How to do It?
Please take a look at the corresponding fiddle:
http://sqlfiddle.com/#!2/16f05/3
You can get your desired output with this query:
select ap.id_price
, p.service
, p.cost_1_1Kg
, p.cost_4_1Kg
, cost_8_1Kg
, group_concat(
concat('account ', ap.id_account)
order by ap.id_account
separator ', '
) as descr
from account_prices ap
inner join prices p using (id_price)
group by ap.id_price
Result:
| ID_PRICE | SERVICE | COST_1_1KG | COST_4_1KG | COST_8_1KG | DESCR |
|----------|---------|------------|------------|------------|---------------------------------|
| 1 | laundry | 1.5 | 2 | 5 | account 1, account 2, account 3 |
| 2 | walk. | 2.5 | 3 | 4 | account 1, account 2, account 3 |
Check updated SQL fiddle
How this works:
You only need to join account_prices with prices to get all the data you need.
To get the account x stuff, concatenate "account " with the value of id_account using the concat() function
Finally, to get a concatenated group of values, use the group_concat() function. It works like any other aggregate function, but instead of performing an operation (like sum() or count()), it concatenates the values of the column (or expression). You can define the order you want for the output and a custom separator (the default separator is ,.
Hope this helps you.
Related
I have some parent and daughter design-wise locations-id in the MySQL database.
Where the daughter linked to the parent. I will show the database design below -
I can able to fetch the data when I search it through daughter location id wise but I don't have any idea how I combined the daughter value when I click parent location.
For example -
MainLocation (123) //total stock 23+10+56= 89
|
|
|---- DaughterLoc1 (456) //suppose stock 23
|
|---- DaughterLoc2 (789) //suppose stock 10 and total stock 10+56 = 66
|
|
|---DaughterLocA (963) //suppose stock 56
SQL : SELECT stock FROM table WHERE location = '456'
OUTPUT = 23 (Corrent)
But I want when searching location 123 I want output 89
My table design is like this below -
table: LocParent
-------------------------
| ID | stock | loc_id |
-------------------------
| 1 | 10 | 789 |
-------------------------
`location`
--------------------------------------------------------------------------------
| ID | main_loc | main_loc_id | loc_under | loc_under_id | stock |
--------------------------------------------------------------------------------
| 1 | MainLocation | 123 | DaughterLoc1 | 456 | 23 |
--------------------------------------------------------------------------------
| 2 | MainLocation | 123 | DaughterLoc2 | 789 | 10 |
--------------------------------------------------------------------------------
It is hard to tell from your sample structure what things actually look like still, and it is further complicated by multiple things called an "id". But, generally speaking, if your depth is finite, you can make small sub-queries, and if your depth is infinite (or unbound) you can make a recursive query.
Here is a sample database. It doesn't match yours, but hopefully it make sense still. If it doesn't, it would help if you provided an actual schema and data (excluding irrelevant columns).
This table is self-referencing to make things easier for demo.
CREATE TABLE sample
(
id int AUTO_INCREMENT NOT NULL,
parent_id INT NULL,
stock int NOT NULL,
PRIMARY KEY (`id`),
CONSTRAINT FOREIGN KEY (`parent_id`) REFERENCES `sample` (`id`)
);
And here's some sample data. There are two records that are "root" and don't have parent values (IDs 1 and 5), two child values (IDs 2 and 3) and one grandchild value (ID 4)
INSERT INTO sample VALUES (1, null, 11);
INSERT INTO sample VALUES (2, 1, 22);
INSERT INTO sample VALUES (3, 1, 33);
INSERT INTO sample VALUES (4, 2, 4);
INSERT INTO sample VALUES (5, null, 55);
Finite/bound
If you have a finite/bound depth, you can make use of subqueries like the below. This one goes to a depth of 3 and sums to 70. Hopefully it is fairly easy to read, but I've included a couple of comments.
SELECT
s.id,
s.stock -- root
+
(
(
SELECT
SUM(c.stock) -- child
FROM
sample c
WHERE
c.parent_id = s.id
)
+
(
SELECT
SUM(p.stock) -- grandchild
FROM
sample c
JOIN
sample p
ON
p.parent_id = c.id
WHERE
c.parent_id = s.id
)
)
as three_level_sum
FROM
sample s
WHERE
s.id = 1;
Infinite/unbound
If you have an infinite hierarchy, however, things get more complicated. MySQL and other database platforms have a thing called "Common Table Expressions" (CTEs) that allow you to make recursive queries. These can be harder to wrap your head around because of the recursion, but it basically does the same as the previous version, just with infinite depth. This version also returns the sum of 70.
WITH RECURSIVE sample_rec AS
(
SELECT
id AS root_id,
id,
parent_id,
stock
FROM
sample
WHERE
parent_id IS NULL
UNION ALL
SELECT
R.root_id,
E.id,
E.parent_id,
E.stock
FROM
sample E
INNER JOIN
sample_rec R
ON
E.parent_id = R.id
)
SELECT
SUM(stock)
FROM
sample_rec
WHERE
root_id = 1
I'd like to know the right and proper practice to handle auto_increment in this table structure problem. Let's say I have these table:
StudentMaster
ID auto_increment
name varchar(100)
Status int(11)
StudentDetail
ID varchar(10)
examID varchar(20)
examDate date
When I insert a data to StudentMaster and StudentDetail in one action, how to make the ID in StudentDetail has the same value with StudentMaster? Because in the new form, there is no textbox containing ID to be inputted.
Data example:
ID Name Status
1 Roy 1
2 Nicole 2
ID examID examDate
1 A1 2017-05-15
1 A2 2017-05-15
1 A5 2017-05-17
2 A2 2017-05-15
1 A3 2017-05-16
P.S: I am in the database structure phase. The PHP form is not created yet.
My head is already spinning from this and I need your help.
MY DATABASE
imported CSV file: 22 columns and 11k rows
2 tables with the same data (both created from the CSV)
Added ID as PRIMARY KEY to both
All VARCHAR(60) Some columns are empty strings ' '
DB:
PID | CODE 1 | CODE 2 | CODE 3 | CODE 4 | CODE 5 | CODE X (up to 9) | ID
-------------------------------------------------------------------------
1 | a | b | c | | | | 1
2 | a | | b | d | | | 2
3 | x | | | | | y | 3
DB has 22 columns but I'm only including CODE columns (up to 9)
in which I might be interested in terms of SQL statement.
It'll be only read table - MyISAM engine then?
WHAT I'D LIKE TO DO
select PID = 1 from first table
and retrieve all PIDs from second table
IF
selected PID's column CODE 1
or
selected PID's column CODE 2 (which is b) etc (up to 9).
= any PID's CODE X
So I should get only PID 2.
edit: PID is not a ID, it's just an example code, it could be string: '002451' and I'm looking for other PIDs with the same CODES (e.g PID1 has code = a so it should find PID2 becasue one of its CODE columns contains a)
MY ATTEMPT
SELECT a.* FROM `TABLE1` a WHERE
(
SELECT * FROM `TABLE2` b WHERE b.`PID` = 1
AND
(
( b.`CODE 1` NOT IN ('') AND IN (a.`CODE 1`,a.`CODE 2`, A.`CODE 3`...) ) OR
( b.`CODE 2` NOT IN ('') AND (a.`CODE 1`,a.`CODE 2`, A.`CODE 3`...) ) OR...
I'd end up with large query - over 81 conditions. In terms of performance... well, it doesn't work.
I intuitively know that I should:
use INDEXES (on CODE 1 / CODE 2 / CODE 3 etc.?)
use JOIN ON (but I'm too stupid) - that's why I created 2 tables (let's assume I don't want TEMP. TABLES)
How to write the SQL / design the DB efficently?
The correct data structure is one row per pid and code. The simplest way is:
create table PCodes (
pid int not null,
code varchar(255),
constraint fk_PCodes_pid references p(pid)
);
Then you have the values in a single column and it is much simpler to check for matching codes.
In practice, you should have three tables:
create table Codes (
CodeId int not null auto_increment primary key,
Code varchar(255)
);
create table PCodes (
pid int not null,
codeid int not null,
constraint fk_PCodes_pid references p(pid),
constraint fk_PCodes_codeid references codes(codeid);
);
If the ordering of the codes is important for each "p", then include a priority or ordering column in the PCodes table.
I want to get the greatest (or lowest) value in a field for a specific value of a different field but I am a bit lost. I am already aware of answered questions on the topic, but I already have a join in my query and I can't apply the terrific answers I found on my specific problem.
I have two tables, namely register and records. Records has all (weather) stations listed once for each month (each stationid represented 12 times, if complete data exists, a stationid can thus not be presented more than 12 times), and register has all stations listed with some of their characteristics. For the sake of the example, the two tables look pretty much like this:
CREATE TABLE IF NOT EXISTS `records` (
`stationid` varchar(30),
`month` int(11),
`tmin` decimal(3,1),
`tmax` decimal(3,1),
`commentsmax` text,
`commentsmin` text,
UNIQUE KEY `webcode` (`stationid`,`month`)
);
INSERT INTO `records` (`stationid`, `month`, `tmin`, `tmax`, `commentsmin`, `commentsmax`) VALUES
('station1', 7, '10.0', '46.0', 'Extremely low temperature.', 'Very high temperature.'),
('station2', 7, '15.0', '48.0', 'Very low temperature.', 'Extremely low temperature.'),
('station1', 1, '-10', '15', 'Extremely low temperature.', 'Somewhat high temperature.');
CREATE TABLE IF NOT EXISTS `register` (
`stationid` varchar(30),
`stationname` varchar(40),
`stationowner` varchar(10),
`georegion` varchar(40),
`altitude` int(4),
KEY `stationid` (`stationid`)
);
INSERT INTO `register` (`stationid`, `stationname`, `stationowner`, `georegion`, `altitude`) VALUES
('station1', 'Halifax', 'Maria', 'the North', 16),
('station2', 'Leeds', 'Peter', 'the South', 240);
The desired output is:
+-------------+-------+-------+---------------+-----------+----------+-----------------------------+
| stationname | month | tmin | stationowner | georegion | altitude | commentsmin |
+-------------+-------+-------+---------------+-----------+----------+-----------------------------+
| Leeds | 7 | 15.0 | Peter | the South | 240 | Very low temperature |
| Halifax | 1 | -10.0 | Maria | the North | 16 | Extremely low temperature |
+-------------+-------+-------+---------------+-----------+----------+-----------------------------+
where each station appears only one with the lowest temperatures from table 'records', including some station properties from the table 'register'. I am using the following code:
SELECT register.stationname, records.month, min(records.tmin), register.stationowner, register.georegion, register.altitude, records.commentsmin FROM records INNER JOIN register ON records.stationid=register.stationid GROUP BY records.stationid ORDER BY min(tmin) ASC
but it doesn't give the correct bits of the records table corresponding to the lowest tmin values BY stationid when there are many records in the tables.
I have seen solutions like this one here: MySQL Greatest N Results with Join Tables, but I just can't get my head around applying it on my two tables. I would be grateful for any ideas!
SELECT stuff
FROM some_table x
JOIN some_other_table y
ON y.something = x.something
JOIN
( SELECT something
, MIN(something_other_thing) min_a
FROM that_other_table
GROUP
BY something
) z
ON z.something = y.something
AND z.min_a = y.another_thing;
I have a CSV file containing user information:
'Arlington', '1,3,5,7,9'
'StackExchange', '2,3'
And I will need the above information imported like this:
"User" table:
id | name
1 | 'Arlington'
2 | 'StackExchange'
"User groups" table:
id | user_id | group_id
1 | 1 | 1
2 | 1 | 3
3 | 1 | 5
4 | 1 | 7
5 | 1 | 9
6 | 2 | 2
7 | 2 | 3
What's the easiest way to do this? I have imported the data with a temp column holding the CSV values:
id | name | tmp_group_ids
1 | 'Arlington' | '1,3,5,7,9'
2 | 'StackExchange' | '2,3'
I am thinking if I import it this way, I will know exactly what id gets assigned for the user (the id column in the users table is auto_increment), and so I can use that id as user_id for the "user groups" table.
But now how do I get values from tmp_group_ids into the "User groups" table?
Would appreciate any help! Thanks!
the easy way would be a php or perl script.
You can use the MySQL SUBSTRING() function to split the string and insert the different values into the table. You can do this by writing a function or using a stored procedure.
I had recently a similar problem, I used the function SUBSTRING_INDEX(str,delim,count), using "," as delimiter
http://dev.mysql.com/doc/refman/5.0/en/string-functions.html#function_substring-index
INSERT INTO tableUserGroup (userid, groupid)
SELECT
t1.id
, substring_index(t1.tmp_group_ids,',',2)
, substring_index(t1.tmp_group_ids,',',3)
FROM table1 t1
First, insert the names into the User table - with id autonumber, this will work:
INSERT INTO User
(name)
SELECT DISTINCT
name
FROM TempTable
Then:
--- Create a "numbers" table:
CREATE TABLE num
( i INT PRIMARY KEY
) ;
--- Populate it with numbers:
INSERT INTO num
(i)
VALUES
(1),(2),(3),(4),(5),(6),(7),(8),(9),(10);
Then, you can use FIND_IN_SET() function which is handy for this situation (splitting comma-separated fields), like this:
INSERT INTO User_Groups
(user_id, group_id)
SELECT
u.id AS user_id
, num.i AS group_id
FROM User AS u
JOIN TempTable AS t
ON t.name = u.name
JOIN num
ON FIND_IN_SET(num.i, t.tmp_group_ids) > 0