Create a view based on two tables with value substitution - mysql

I struggle with a query and I am not sure how to do this.
I would like to create a view my_view based on the table original_data_table where each values are substituted by non-null values from the table replacement_data_table whenever an id is specified.
Both original_data_table and replacement_data_table have the same structure but with different values.
I tried to use JOIN in my query but I'm not sure if it's the way to go.
SELECT * FROM original_data_table AS o
LEFT JOIN replacement_data_table AS r
ON o.id = r.id
original_data_table
id name value
1 David 10
2 John 20
3 Sarah 30
4 Amy 40
replacement_data_table
id name value
1 NULL 50
2 Rick NULL
4 Emma 60
my_view
id name value
1 David 50
2 Rick 20
3 Sarah 30
4 Emma 60

You need COALESCE() to get the right value from the right table:
SELECT o.id,
COALESCE(r.name, o.name) as name,
COALESCE(r.value, o.value) as value
FROM original_data_table o LEFT JOIN
replacement_data_table r
ON o.id = r.id;

Try this:
SELECT o.id as id, IFNULL(r.name, o.name) as new_name, IFNULL(r.value, o.value) as new_value
FROM original_data_table AS o
LEFT JOIN replacement_data_table AS r
ON o.id = r.id

Try it use case when
SELECT
o.`id`,case when o.`name` <> r.`name` and r.`name` is not null then
r.`name`
else o.`name`
end as name
,case when o.`value` <> r.`value` and r.`value` is not null then
r.`value`
else o.`value`
end as value
FROM original_data_table AS o
LEFT JOIN replacement_data_table AS r
ON o.id = r.id
order by o.id
SQL Fiddle Demo Link
| id | name | name |
|----|-------|------|
| 1 | David | 50 |
| 2 | Rick | 20 |
| 3 | Sarah | 30 |
| 4 | Emma | 60 |

Related

How i implement a query to display data by using 3 tables data which are depend on each other?

My Three table are like this
Requistion Item Sub_Category
id | Item_id | status Item_id | name | sub_cat sub_cat | Sub_name
1 | 10 | 100 10 | apple |1 1 | Fruits
2 | 20 | 100 20 | Beans |2 2 | Vegitable
3 | 30 | 100 30 | banana |1 3 | Drinks
4 | 40 | 200 40 | Water |3
I want to Display all Sub_name With below condition
Requistion Table status=100
Result Without Duplicate names
As per the above tables Result must be like
Sub_name
Fruits
Vegitable
Using DISTINCT Sub_name with JOINs this can be done:
SELECT DISTINCT S.Sub_name
FROM Requistion R
JOIN Item I
ON I.Item_id = R.Item_id
JOIN Sub_Category S
ON S.sub_cat = I.sub_cat
WHERE R.status = 100
SELECT * FROM Requistion INNER JOIN Item on Item.Item_id=Requistion.Item_id INNER JOIN Sub_Category on Sub_Category.sub_cat=Item.sub_cat where STATUS=100
You can use the below query to get this using group by to get unique sub_name,
select Sub_name from Sub_Category sc
JOIN Item i on i.sub_cat = sc.sub_cat
JOIN Requistion r on r.Item_id = i.Item_id
WHERE r.status = 100
GROUP BY sc.Sub_name
You can use distinct for the same,
select DISTINCT Sub_name from Sub_Category sc
JOIN Item i on i.sub_cat = sc.sub_cat
JOIN Requistion r on r.Item_id = i.Item_id
WHERE r.status = 100

Mysql select query for 3 tables

I have a 3 tables order, order_option, product_option
order
order_id | cus_name | cus_phone
-------------------------------
1 | Test-1 | 9876543211
2 | Test-2 | 9876543212
3 | Test-3 | 9876543213
4 | Test-4 | 9876543214
order_option
product_option_id | order_id
-------------------------------
11 | 1
12 | 1
13 | 2
14 | 4
15 | 3
product_option
product_id | product_option_id | sku | qty
------------------------------------------
1 | 11 | TS01 | 3
2 | 12 | TS02 | 2
3 | 13 | TS033 | 3
4 | 14 | TS023 | 3
Here I want to select order table and product_option table values with a where condition on the sku field.
i tried to join the query like below:
SELECT o.order_id, o.cus_name, o.cus_phone,po.sku,po.qty FROM order o
LEFT JOIN order_option op
ON (o.order_id = op.order_id)
LEFT JOIN product_option po
ON (op.product_option_id = po.product_option_id)
WHERE po.sku = "TS023"
But it's not showing the correct answer. I don't know what I have missed.
order is a reserved word, use backticks ``.
SELECT o.order_id, o.cus_name, o.cus_phone, po.sku, po.qty
FROM `order` o
LEFT JOIN order_option op ON o.order_id = op.order_id
LEFT JOIN product_option po ON op.product_option_id = po.product_option_id
WHERE po.sku = "TS023"
Output:
order_id cus_name cus_phone sku qty
4 Test-4 9876543214 TS023 3
SQL Fiddle: http://sqlfiddle.com/#!9/9b76b/2/0
Move the po condition from WHERE to ON to get true LEFT JOIN result:
SELECT o.order_id, o.cus_name, o.cus_phone,po.sku,po.qty FROM order o
LEFT JOIN order_option op
ON (o.order_id = op.order_id)
LEFT JOIN product_option po
ON (op.product_option_id = po.product_option_id)
AND po.sku = "TS023"
(When in WHERE, you'll get regular INNER JOIN result.)
#Matt is correct, here's another way of doing this.
SELECT o.order_id, o.cus_name, o.cus_phone, po.sku, po.qty
FROM `order` o, order_option op, product_option po
WHERE o.order_id = op.order_id
AND op.product_option_id = po.product_option_id
AND po.sku = "TSO23"

Mysql query to get records from different columns for the id

Following is the details for my table structure.
Table structure "departments":
id | department
1 | Department 1
2 | Department 2
3 | Department 3
4 | Department 4
Table structure "custom_forms_departments":
id | form_id | department_id | enduser_to_department | department_to_enduser
1 | 5 | 1 | Y | N
2 | 6 | 1 | N | Y
3 | 8 | 2 | Y | Y
4 | 7 | 3 | N | Y
5 | 4 | 3 | Y | N
6 | 2 | 4 | N | N
Result should be return department_id which have the vaue "Y" for field "enduser_to_department" and "department_to_enduser" in same row or in different row.
department_id = 1 contatin value "Y" for "enduser_to_department" and "department_to_enduser" in different rows
department_id = 2 contatin value "Y" for "enduser_to_department" and "department_to_enduser" in same rows
department_id = 3 contatin value "Y" for "enduser_to_department" and "department_to_enduser" in different rows
Result:
department_id | departments
1 | Department 1
2 | Department 2
3 | Deapartment 3
I am using following SQL query which is not giving proper result.
SELECT departments.department_id, departments.department
FROM custom_forms_departments , departments
WHERE departments.department_id = custom_forms_departments.department_id
AND (custom_forms_departments.enduser_to_department = 'Y'
OR custom_forms_departments.department_to_enduser = 'Y')
GROUP BY departments.department_id
ORDER BY departments.department_id DESC
Please suggest me for this one.
First try to take care of custom_forms_departments.
Way: Create 2 copies of custom_forms_departments (c1 and c2). You'll want to JOIN them, based on (c1.department_id = c2.department_id) -simple, and (c1.enduser_to_department = c2.department_to_enduser) - because you want to get only rows that have both 'Y' in them (will filter the 'Y' in the WHEN, but for now, you'll get any rows that have same value in both columns). Second, use the WHEN to filter only 'Y'.
SELECT
custom_forms_departments.department_id
FROM
custom_forms_departments c1
INNER JOIN
custom_forms_departments c2 ON c1.department_id = c2.department_id
AND c1.enduser_to_department = c2.department_to_enduser
WHERE
c1.enduser_to_department = 'Y'
GROUP BY
c1.department_id
;
Now we've got the "complicated" staff, lets gather all together and add the departments columns:
SELECT
departments.department_id, departments.department
FROM
departments
INNER JOIN
(SELECT
custom_forms_departments.department_id
FROM
custom_forms_departments c1
INNER JOIN custom_forms_departments c2 ON c1.department_id = c2.department_id
AND c1.enduser_to_department = c2.department_to_enduser
WHERE
c1.enduser_to_department = 'Y'
GROUP BY
c1.department_id) c3 ON departments.department_id = c3.department_id
;
Use the following query. Also always use explicit JOIN
SELECT d.department_id, d.department
FROM custom_forms_departments AS cfd
INNER JOIN departments AS d ON d.department_id = cfd.department_id
AND (cfd.enduser_to_department = 'Y' OR cfd.department_to_enduser = 'Y')
GROUP BY d.department_id, d.department
ORDER BY d.department_id
SELECT id as department_id, department from departments WHERE id IN
(
SELECT DISTINCT(departament_id) FROM custom_forms_departments WHERE enduser_to_department = 'Y' OR department_to_enduser = 'Y'
)

Compare variation between first and second row for group

I got a "Empresas" table
dbo.empresas
id | name | delegacion_id
-------------------------
1 | A | 3
2 | B | 3
3 | C | 3
4 | D | 4
a "pagos" table
dbo.pagos
id | id_empresa | monto | periodo
----------------------------------
1 | 1 | 120 | 2012-11-01
2 | 1 | 125 | 2012-12-01
3 | 2 | 150 | 2012-11-01
4 | 1 | 200 | 2013-01-01
5 | 2 | 151 | 2012-12-01
I have a value X that is a percentage.
I need to show the "empresas" that, comparing the "montos" of their two last "pagos" (ordered by periodo), have changed at least +X% or -X%, from an especific id_delegacion
For example, if we run this query with these example values, considering
X = 10
id_delegacion = 3
the output expected will be:
name | periodo | monto
---------------------------
A | 2012-12-01 | 125
A | 2013-01-01 | 200
empresa A is from delegacion_id = 3, and the comparison between the last two pagos, ordered by periodo desc (200 => 125) is bigger than 10%.
B is not showed because the comparison is smaller than 10%.
C is not showed because has no row in "pagos" table
D is from another delegation.
How can I get this desired output? For the record, using MySQL 5.5.8.
What I've done
I got this
select P.id_empresa, max(periodo) as periodo from
pagos P
where id_empresa in(
select e.id
from empresa E
where E.id_delegacion = 3
)
group by p.id_empresa, p.periodo
having count(*) > 1
with these I got the "empresas" that have more than one "pago" row, and got id_delegation = 3.
Also get the first period (the maximum), but I don't know how to get the second for each empresa, and compare them.
thanks
This is my query:
SELECT
empresas.name,
pagos.periodo,
pagos.monto
FROM
pagos INNER JOIN (
SELECT
lst.id id1,
prc.id id2
FROM (
SELECT
p1.id_empresa,
MAX(p1.periodo) last_p,
MAX(p2.periodo) prec_p
FROM
pagos p1 INNER JOIN pagos p2
ON p1.id_empresa = p2.id_empresa
AND p2.periodo < p1.periodo
GROUP BY
id_empresa) latest
INNER JOIN
pagos lst ON lst.id_empresa = latest.id_empresa AND lst.periodo=latest.last_p
INNER JOIN
pagos prc ON prc.id_empresa = latest.id_empresa AND prc.periodo=latest.prec_p
WHERE
lst.monto > prc.monto * 1.1) ids
ON pagos.id IN (ids.id1, ids.id2)
INNER JOIN
empresas
ON pagos.id_empresa = empresas.id
WHERE
delegacion_id=3
I think it can be simplified if you want to have values on the same row, e.g.
name | ultimo_periodo | ultimo_monto | anterior_periodo | anterior_monto
Please see fiddle here.
I still wondering if it can be simplified a little, but I am not sure if it is. Here's another solution:
SELECT
empresas.name,
pagos.periodo,
pagos.monto
FROM
pagos INNER JOIN empresas
ON pagos.id_empresa = empresas.id
INNER JOIN (
SELECT
id_empresa,
MAX(CASE WHEN row=1 THEN monto END) lst_monto,
MAX(CASE WHEN row=2 THEN monto END) prc_monto,
MAX(id) id1, MIN(id) id2
FROM (
SELECT
p1.*, COUNT(*) row
FROM
pagos p1 INNER JOIN pagos p2
ON p1.id_empresa = p2.id_empresa
AND p1.periodo <= p2.periodo
INNER JOIN empresas
ON p1.id_empresa = empresas.id
WHERE
empresas.delegacion_id = 3
GROUP BY
p1.id, p1.id_empresa, p1.monto, p1.periodo
HAVING
COUNT(*)<=2
ORDER BY
p1.id_empresa, p1.periodo desc
) s
GROUP BY
id_empresa
HAVING
lst_monto>prc_monto*1.1
) l ON pagos.id IN (l.id1, l.id2)
Please see fiddle here.

Finding the MIN value that appears for each unique value in either of two other columns

Given the following (simplified) tables:
People p
id name registered
-----------------------------------
1 Geoff 2011-03-29 12:09:08
2 Phil 2011-04-29 09:03:54
3 Tony 2011-05-29 21:22:23
4 Gary 2011-06-21 22:56:08
...
Items i
date p1id p2id
----------------------------------------
2011-06-29 20:09:44 1 2
2011-06-26 10:45:00 1 3
2011-06-23 12:22:43 2 3
2011-06-22 13:07:12 2 4
...
I'd like:
The earliest single i.date that each p.id appears in either column p1id or p2id; or p.registered if they feature in neither.
So far, I've tried:
CREATE TEMPORARY TABLE temp (id INT);
INSERT INTO temp (id)
SELECT DISTINCT u FROM (
SELECT p1id AS u FROM Items UNION ALL
SELECT p2id AS u FROM Items
)tt;
SELECT registered,id FROM People
WHERE id NOT IN (SELECT id FROM temp);
Which gets me as far as the second part, albeit in a fairly clumsy way; and I'm stuck on the first part beyond some sort of external, scripted iteration through all the values of p.id (ugh).
Can anyone help?
I'm on MySQL 5.1 and there's ~20k people and ~100k items.
One more solution:
SELECT id, name, IF(min_date1 IS NULL AND min_date2 IS NULL, registered, LEAST(COALESCE(min_date1, min_date2), COALESCE(min_date2, min_date1))) date FROM (
SELECT p.id, p.name, p.registered, MIN(i1.date) min_date1, MIN(i2.date) min_date2 FROM people p
LEFT JOIN items i1
ON p.id = i1.p1id
LEFT JOIN items i2
ON p.id = i2.p2id
GROUP BY id
) t;
OR this:
SELECT p.id, p.name, COALESCE(MIN(i.date), p.registered) FROM people p
LEFT JOIN (
SELECT p1id id, date FROM items
UNION ALL
SELECT p2id id, date FROM items
) i
ON p.id = i.id
GROUP BY id;
Result:
+------+-------+---------------------+
| id | name | date |
+------+-------+---------------------+
| 1 | Geoff | 2011-06-26 10:45:00 |
| 2 | Phil | 2011-06-22 13:07:12 |
| 3 | Tony | 2011-06-23 12:22:43 |
| 4 | Gary | 2011-06-22 13:07:12 |
+------+-------+---------------------+
This is tested in Postgres, but I think it ought to work in MySQL with few or no changes:
SELECT p.id,COALESCE(MIN(x.date),p.registered) AS date
FROM p
JOIN (
SELECT p.id,MIN(i.date) AS date
FROM p
JOIN i ON (p.id=i.p1id)
GROUP BY p.id
UNION
SELECT p.id,MIN(i.date) AS date
FROM p
JOIN i ON (p.id=i.p2id)
GROUP BY p.id
) AS x ON x.id = p.id
GROUP BY p.id,p.registered;
Output (given your sample data):
id | date
----+---------------------
3 | 2011-06-23 12:22:43
1 | 2011-06-26 10:45:00
2 | 2011-06-22 13:07:12
4 | 2011-06-22 13:07:12
(4 rows)