i am looking for a better way to handle the following problem.
custData
--------
ID | Code
1 | A
2 | B
prodData
--------
ID | ProdID
1 | prodA
2 | prodB
3 | prodC
and i would like to keep a record in this table:
custProdPrice
--------
ID | Cust | Prod
1 | A | prodA
2 | A | prodB
3 | A | prodC
4 | B | prodA
5 | B | prodB
6 | B | prodC
i was getting it done by using the following query
create view CashPOSDB.viewertable as select Code, ProdID as pid, BelongTo
from CashPOSDB.custData cross join CashPOSDB.prodData
AND
insert into CashPOSDB.custProdPrice(Cust, Prod, BelongTo)(select Code, Pid, BelongTo from
(select Code, pid, belongTo from CashPOSDB.viewertable UNION ALL
select Cust, Prod, BelongTo from CashPOSDB.custProdPrice) t group by Code, Pid);
It does the job, but it seems like its missing some result sometimes or inserting duplicate rows.
Also, I would like it to check so that when I insert a new Prod prodD, it would check in custProdPrice and see if cust A and B already has a row of
A | prodD
b | prodD
and insert it if it doesn't exist.
So what would the best way be to optimize this?
Give the custProdPrice table a unique index on (Cust, Prod) to prevent duplicates:
ALTER TABLE custProdPrice ADD UNIQUE INDEX (Cust, Prod);
Then use INSERT IGNORE to skip duplicates.
There doesn't seem to be any reason to UNION with custProdPrice when inserting into custProdPrice. Just insert the cross product.
INSERT IGNORE INTO CashPOSDB.custProdPrice (Cust, Prod, BelongTo)
SELECT Cust, Prod, BelongTo
FROM CashPOSDB.viewertable
To automatically add new rows to custProdPrice, use a trigger:
CREATE TRIGGER addCustProdPrice
AFTER INSERT ON prodTable
FOR EACH ROW
INSERT IGNORE INTO custProdPrice (Cust, Prod, BelongTo)
SELECT c.Cust, NEW.Prod, NEW.BelongTo
FROM custData;
Related
I am using MySql.
I have table job that has a primary key job_pk_id and the table stores details of every job. Also I have a table job_running_status where job table's job_pk_id is a foreign key and this table basically contains records of when a job ran for each job_pk_id.There will be multiple entries for the same job_pk_id as the same job runs multiple times. job_running_status table also has a field job_start_time that gives the start time for each instance of the running of the job.
Now my requirement is to get the latest job_running_status for every job . The latest job_running_status would be chosen based on the latest job_start_time(for that particular job only) value in job_running_status.
I know this can be achieved using INNER JOIN and ORDER BY job_start_time desc between job table and job_running_status table but my challenge is this ORDER BY becomes applicable across all the jobs in the JOIN but I need to be applicable only across records that are corresponding to a particular job.
EDIT
I understand it might be confusing to understand me by just reading so I am providing some examples:
job table:
job_running_status table:
My final requirement after joining both the tables
Note: while joining I a should be getting only 1 record corresponding to every JOB table record. This record is chosen based on the latest job_start_time for that JOB.
An example of a correlated sub query in a where clause
drop table if exists t,t1;
create table t(id int);
create table t1(jid int,dt date);
insert into t values
(1),(2),(3);
insert into t1 values
(1,'2018-01-01'),
(1,'2018-02-01'),
(2,'2018-01-01'),
(3,'2018-01-01'),
(3,'2018-02-01'),
(3,'2018-03-01');
select t.id,t1.dt
from t
join t1 on t1.jid = t.id
where t1.dt =(select max(dt) from t1 where t1.jid = t.id);
+------+------------+
| id | dt |
+------+------------+
| 1 | 2018-02-01 |
| 2 | 2018-01-01 |
| 3 | 2018-03-01 |
+------+------------+
3 rows in set (0.00 sec)
If you need the latest n records and you are not on version 8.0 or higher you can use row number simulation
select t.id,s.dt
from t
join
(select t1.jid,t1.dt ,
if(t1.jid<>#p,#rn:=1,#rn:=#rn+1) rn,
#p:=t1.jid p
from t1
cross join (select #rn:=0,#p:=0) r
order by t1.jid ,t1.dt desc
) s on s.jid = t.id
where s.rn <= 2;
+------+------------+
| id | dt |
+------+------------+
| 1 | 2018-01-01 |
| 1 | 2018-02-01 |
| 2 | 2018-01-01 |
| 3 | 2018-02-01 |
| 3 | 2018-03-01 |
+------+------------+
You can try this query. CROSS JOIN with subquery, which get MAX(job_running_status)
Then join job and job_running_status tables.
TestDLL
CREATE TABLE JOB(
job_pk_id int
);
INSERT INTO JOB VALUES (1),(2),(3);
CREATE TABLE job_running_status(
fk_job_id INT,
job_running_status DATE
);
INSERT INTO job_running_status VALUES (1,'2018-01-01');
INSERT INTO job_running_status VALUES (1,'2018-02-01');
INSERT INTO job_running_status VALUES (2,'2018-01-03');
INSERT INTO job_running_status VALUES (2,'2018-01-02');
Query
SELECT DISTINCT
j.job_pk_id,
jrs.fk_job_id,
t.job_running_status
FROM
(SELECT MAX(job_running_status) job_running_status FROM job_running_status) t
CROSS JOIN job j
inner join job_running_status jrs on j.job_pk_id = jrs.fk_job_id
[Results]:
| job_pk_id | fk_job_id | job_running_status |
|-----------|-----------|--------------------|
| 1 | 1 | 2018-02-01 |
| 2 | 2 | 2018-02-01 |
sqlfiddle
I have a table as follows. What I would like to avoid is having two product id's in the table. How can I merge the two common fields using a query and increment the quantity?
cartid | prodid | quanity |
1 | 9226582 | 3 |
2 | 9226582 | 5 |
3 | 7392588 | 1 |
The desired results is that the table should be altered as follows:
cartid | prodid | quanity |
1 | 9226582 | 8 |
3 | 7392588 | 1 |
I have searched for answers but all seem too complex. Is there a way to do this in a simple way?
If you want to update the table in the database, you can do this:
create table newtable
(`cartid` int, `prodid` int unique key, `quantity` int);
insert into newtable
select * from yourtable order by cartid
on duplicate key update quantity=newtable.quantity+values(quantity)
select * from newtable
Output:
cartid prodid quantity
1 9226582 8
3 7392588 1
If you're happy with the result you can then
drop table yourtable
alter table newtable rename to yourtable
Use group by and min-
check this-http://sqlfiddle.com/#!9/6c4332/4
select min(cartid) cartid ,prodid,sum(quantity) quantity
from
yourtable
group by prodid
order by cartid
create another table with same schema,
then
insert into newtable
select min(cartid) cartid ,prodid,sum(quantity) quantity
from
yourtable
group by prodid
order by cartid
Rename the newtable
For fast searching table , I need to separate a large table to two tables
example table:
+--------+--------+-------+------+
| source | target | count | prob |
+--------+--------+-------+------+
| test1 | test2 | 2 | 1 |
| cat | dog | 3 | 1.5|
| dog | cat | 1 | 0.5|
+--------+--------+-------+------+
Using the code below
INSERT INTO Table2 (source,target,count,prob)
SELECT source,target,count,prob FROM Table1 WHERE count <2;
then delete originals
DELETE FROM Table1 WHERE count<2;
And count will grouping up after separating table in Table1, and new same element will increase after separating.
For example:
source = 'dog' and target = 'cat' and count = 1 will be move to Table2 and Table1 will still grouping up like add the count or will be add new row source = 'dog' target ='cat' , count = 3.
How could I combine Table1 and Table2 (Table2 will not change after separating)
You can combine the result with UNION
SELECT source, target, count, prob FROM tbl1
UNION
SELECT source, target, count, prob FROM tbl2
Just note there are lots of better ways to get better performance on large tables
How do I go about creating a table from multiple tables having variable columns (union cannot be used)?
mysql> select * from a;
+------+------+
| id | name |
+------+------+
| 1 | Ruby |
+------+------+
mysql> select * from b;
+------+------+
| pid | name |
+------+------+
| 1 | Java |
| 1 | C++ |
+------+------+
What I want...
+------+------+------+------+
| id | name | pid | bname|
+------+------+------+------+
| 1 | Ruby | | |
| | | 1 | C++ |
| | | 1 | Java |
+------+------+------+------+
What I have tried.
mysql> create table t as select * from a; # worked fine
mysql> alter table t as select pid, name as bname from b; #didn't work
You can do this in this rather arcane way:
select a.*, b.*
from a left join
b
on 1 = 0
union all
select a.*, b.*
from b left join
a
on 1 = 0;
This is one way, in MySQL of implementing:
select a.*, b.*
from a full outer join
b
on 1 = 0;
But, it requires a union. You could do it in two steps if you want to avoid the union:
create table ab as
select a.*, b.*
from a left join
b
on 1 = 0;
insert into ab
select a.*, b.*
from b left join
a
on 1 = 0;
There's no problem to use union, you just need to select nulls as the columns that aren't in the table:
CREATE TABLE t AS
SELECT id, name, null AS pid, null AS bname
FROM a
UNION ALL
SELECT null, null, pid, bname
FROM b
Create the new table, containing all four columns. Then
INSERT INTO t (id, name) SELECT * FROM a;
INSERT INTO t (pid, bname) SELECT * FROM b;
The first line will put everything from a in there, and leave the other two fields as NULL; the second will do the corresponding thing from b. You could do the whole thing in one CREATE/UNION statement, but if this is a one-off operation, it's probably easier to understand what you're doing if you do it in stages like this.
You can try this this as an example.
Create a new table (name, date of birth) by joining two tables (student id, name) and (student id, date of birth).
Create first table:
create table stu1(student_id char(5) primary key,name varchar(45))ENGINE=innodb;
Create second table:
create table stu2(student_id char(5) primary key,dob date)ENGINE=innodb;
Create third table (by joining two tables):
create table stu3 AS(select stu1.name,stu2.dob from stu1,stu2
where stu1.student_id=stu2.student_id);
Had a good read through similar topics but I can't quite a) find one to match my scenario, or b) understand others enough to fit / tailor / tweek to my situation.
I have a table, the important fields being;
+------+------+--------+--------+
| ID | Name | Price |Status |
+------+------+--------+--------+
| 1 | Fred | 4.50 | |
| 2 | Fred | 4.50 | |
| 3 | Fred | 5.00 | |
| 4 | John | 7.20 | |
| 5 | John | 7.20 | |
| 6 | John | 7.20 | |
| 7 | Max | 2.38 | |
| 8 | Max | 2.38 | |
| 9 | Sam | 21.00 | |
+------+------+--------+--------+
ID is an auto-incrementing value as records get added throughout the day.
NAME is a Primary Key field, which can repeat 1 to 3 times in the whole table.
Each NAME will have a PRICE value, which may or may not be the same per NAME.
There is also a STATUS field that need to be populated based on the following, which is actually the part I am stuck on.
Status = 'Y' if each DISTINCT name has only one price attached to it.
Status = 'N' if each DISTINCT name has multiple prices attached to it.
Using the table above, ID's 1, 2 and 3 should be 'N', whilst 4, 5, 6, 7, 8 and 9 should be 'Y'.
I think this may well involve some form of combination of JOINs, GROUPs, and DISTINCTs but I am at a loss on how to put that into the right order for SQL.
In order to get the count of distinct Price values per name, we must use a GROUP BY on the Name field, but since you also want to display all names ungrouped but with an additional Status field, we must first create a subselect in the FROM clause which groups by the name and determines whether the name has multiple price values or not.
When we GROUP BY Name in the subselect, COUNT(DISTINCT price) will count the number of distinct price values for each particular name. Without the DISTINCT keyword, it would simply count the number of rows where price is not null.
In conjunction with that, we use a CASE expression to insert N into the Status column if there is more than one distinct Price value for the particular name, otherwise, it will insert Y.
The subselect only returns one row per Name, so to get all names ungrouped, we join that subselect to the main table on the condition that the subselect's Name = the main table's Name:
SELECT
b.ID,
b.Name,
b.Price,
a.Status
FROM
(
SELECT Name, CASE WHEN COUNT(DISTINCT Price) > 1 THEN 'N' ELSE 'Y' END AS Status
FROM tbl
GROUP BY Name
) a
INNER JOIN
tbl b ON a.Name = b.Name
Edit: In order to facilitate an update, you can incorporate this query using JOINs in the UPDATE like so:
UPDATE
tbl a
INNER JOIN
(
SELECT Name, CASE WHEN COUNT(DISTINCT Price) > 1 THEN 'N' ELSE 'Y' END AS Status
FROM tbl
GROUP BY Name
) b ON a.Name = b.Name
SET
a.Status = b.Status
Assuming you have an unfilled Status column in your table.
If you want to update the status column, you could do:
UPDATE mytable s
SET status = (
SELECT IF(COUNT(DISTINCT price)=1, 'Y', 'N') c
FROM (
SELECT *
FROM mytable
) s1
WHERE s1.name = s.name
GROUP BY name
);
Technically, it should not be necessary to have this:
FROM (
SELECT *
FROM mytable
) s1
but there is a mysql limitation that prevents you to select from the table you're updating. By wrapping it in parenthesis, we force mysql to create a temporary table and then it suddenly is possible.