mysql only insert row if value does not exists [duplicate] - mysql

This question already has answers here:
MySQL Conditional Insert
(13 answers)
Closed 8 years ago.
A complex mysql question! I only want to insert the last value (with the zero values) if there is no other row with value 1420070400, but i cant put an index on the row (so i can use on duplicate key). Is there a way to do this on an other way?
INSERT INTO data_prijzen_advertentie (
`ID_advertentie`,`jaar`,`rijnr`,`status_prijs`,`datum_dag`,`timestamp_dag`,
`prijs_maand`,`prijs_week`,`prijs_midweek`,`prijs_langweekend`,`prijs_weekend`,
`prijs_dag`,`prijs_ochtend`,`prijs_middag`
)
VALUES
(100,2014,1,1,'12-05-2014',1399852800,0,100,0,75,0,0,0,0),
(100,2014,2,1,'23-05-2014',1400803200,0,75,0,101,0,0,0,0),
(100,2014,3,1,'30-05-2014',1401408000,0,100,0,75,0,0,0,0),
(100,2014,4,1,'01-01-2015',1420070400,0,0,0,0,0,0,0,0)
ON DUPLICATE KEY UPDATE
status_prijs = VALUES(status_prijs), datum_dag = VALUES(datum_dag),
timestamp_dag = VALUES(timestamp_dag), prijs_maand = VALUES(prijs_maand),
prijs_week = VALUES(prijs_week), prijs_midweek = VALUES(prijs_midweek),
prijs_langweekend = VALUES(prijs_langweekend), prijs_weekend = VALUES(prijs_weekend),
prijs_dag = VALUES(prijs_dag), prijs_ochtend = VALUES(prijs_ochtend),
prijs_middag = VALUES(prijs_middag);

One way to do this is to use a SELECT in place of the VALUES clause. Use the SELECT statement to return the rows you want inserted. For example:
SELECT 100 AS a,2014 AS b,1 AS c,1 AS d,'12-05-2014' AS e
,1399852800 AS timestamp_dag
,0 AS g,100 AS h,0 AS i,75 AS j,0 AS k,0 AS l,0 AS m,0 AS n
UNION ALL
SELECT 100,2014,2,1,'23-05-2014',1400803200,0,75,0,101,0,0,0,0
UNION ALL
SELECT 100,2014,3,1,'30-05-2014',1401408000,0,100,0,75,0,0,0,0
UNION ALL
SELECT 100,2014,4,1,'01-01-2015',1420070400,0,0,0,0,0,0,0,0
With a SELECT, it's also possible to check for rows that already exist in the table. You can use that query above as an inline view (i.e. wrap that query in parens and assign an alias for the derived table) and write a SELECT against it (like it was a table), and use a NOT EXISTS predicate or an anti-join pattern to suppress the return of rows where a "matching" row already exists in the target table. For example:
SELECT s.*
FROM ( SELECT 100 AS a,2014 AS b,1 AS c,1 AS d,'12-05-2014' AS e
,1399852800 AS timestamp_dag
,0 AS g,100 AS h,0 AS i,75 AS j,0 AS k,0 AS l,0 AS m,0 AS n
UNION ALL
SELECT 100,2014,2,1,'23-05-2014',1400803200,0,75,0,101,0,0,0,0
UNION ALL
SELECT 100,2014,3,1,'30-05-2014',1401408000,0,100,0,75,0,0,0,0
UNION ALL
SELECT 100,2014,4,1,'01-01-2015',1420070400,0,0,0,0,0,0,0,0
) s
LEFT
JOIN data_prijzen_advertentie t
ON t.timestamp_dag = s.timestamp_dag
WHERE t.timestamp_dag IS NULL
(NOTE: the LEFT JOIN operation returns all rows from the derived table s, along with matching rows from t; the "trick" is to use a WHERE clause that eliminates all rows that found a match, so we are left with rows from s that didn't have a matching row in t. The same thing could be achieved with a NOT EXISTS predicate with a correlated subquery.)
It may be necessary to wrap this query in parens and reference it as an inline view (so it is again, a derived table) to avoid a mutating table issue/error. For example:
SELECT r.*
FROM (
SELECT s.*
FROM (
SELECT 100 AS a,2014 AS b,1 AS c,1 AS d,'12-05-2014' AS e
,1399852800 AS timestamp_dag
,0 AS g,100 AS h,0 AS i,75 AS j,0 AS k,0 AS l,0 AS m,0 AS n
UNION ALL
SELECT 100,2014,2,1,'23-05-2014',1400803200,0,75,0,101,0,0,0,0
UNION ALL
SELECT 100,2014,3,1,'30-05-2014',1401408000,0,100,0,75,0,0,0,0
UNION ALL
SELECT 100,2014,4,1,'01-01-2015',1420070400,0,0,0,0,0,0,0,0
) s
LEFT
JOIN data_prijzen_advertentie t
ON t.timestamp_dag = s.timestamp_dag
WHERE t.timestamp_dag IS NULL
) r
Once you have a query working that returns the rows you want (excluding rows where a matching row already exists in the target table), you can substitute the VALUES clause in the INSERT statement with the query.
Note: this only checks for existence of rows already in the table when the query runs. This doesn't check the resultset returned by the query, to see if there are two (or more) rows with the timestamp_dag value.

Related

Select IN with an array with duplicate value on it

I am trying to make a request where I select from an array of value using the IN, but inside this array, if I have the same value twice, I'd like the request to return the result twice.
To clarify, here is an example:
select id_exo, count(id_exo) FROM BLOC WHERE id_seance IN (10,10) group by id_exo
So inside the IN, I put 2 times the value 10.
Here is the result:
id_exo
count(id_exo)
60
1
82
1
But in count, I'd like to have the number 2 since I have put twice 10 inside my IN.
How can I achieve that?
SELECT id_exo, COUNT(id_exo)
FROM bloc
JOIN (SELECT 10 id_seance
UNION ALL
SELECT 10) val USING (id_seance)
GROUP BY id_exo
Prior to MySQL 8.0 you can join with a sub select:
select * from BLOC as b
inner join (
select 1 as 'id', 10 as 'value'
union
select 2,10
union
select 3,10) as myValues on myValues.value = b.id_seance
You need the id column as the UNION statement removes duplicate rows
If you are lucky enough to have MySQL 8.0 look at the VALUES statement
https://dev.mysql.com/doc/refman/8.0/en/values.html
Here you should instead be able to join with something like
VALUES ROW(10), ROW(10), ROW(10)

How to find which values are not in a column from the list?(SQL) [duplicate]

This question already has an answer here:
Select values from a list that are not in a table
(1 answer)
Closed 2 years ago.
I have a list of values:
('WEQ7EW', 'QWE7YB', 'FRERH4', 'FEY4B', .....)
and the dist table with a dist_name column.
and I need to create SQL query which would return values from the list which don't exist in the dist_name column.
Yo need to use left join. This requires creating a derived table with the values you care about. Here is typical syntax:
select v.val
from (values ('WEQ7EW'), ('QWE7YB'), ('FRERH4'), ('FEY4B')
) v(val) left join
t
on t.col = v.val
where t.col is null;
Not all databases support the values() table constructor but allow allow some method for creating a derived table. In MySQL, this looks like:
select v.val
from (select'WEQ7EW' as val union all
select 'QWE7YB' as val union all
select 'FRERH4' as val union all
select 'FEY4B' as val
) v(val) left join
t
on t.col = v.val
where t.col is null;
You would typically put this list of values in a derived table, and then use not exists. In MySQL:
select v.dist_name
from (
select 'WEQ7EW' as dist_name
union all select 'QWE7YB'
union all ...
) v
where not exists (select 1 from dist d where d.dist_name = v.dist_name)
Or if you are running a very recent version (8.0.19 or higher), you can use the VALUES ROW() syntax:
select v.dist_name
from (values row('WEQ7EW'), row('QWE7YB'), ...) v(dist_name)
where not exists (select 1 from dist d where d.dist_name = v.dist_name)
SELECT TRIM(TRAILING ',' FROM result) result
FROM ( SELECT #tmp:=REPLACE(#tmp, CONCAT(words.word, ','), '') result
FROM words, (SELECT #tmp:='WEQ7EW,QWE7YB,FRERH4,FEY4B,') arg
) perform
ORDER BY LENGTH(result) LIMIT 1;
fiddle
The list of values to be cleared from existing values is provided as CSV string with final comma and without spaces before/after commas ('WEQ7EW,QWE7YB,FRERH4,FEY4B,' in shown code).
If CSV contains duplicate values all of them will be removed whereas non-removed duplicates won't be compacted. The relative arrangement of the values will stay unchanged.
Remember that this query performs full table scan, so it is not applicable to huge tables because it will be slow.

Sql select where array in column

In my query I use join table category_attributes. Let's assume we have such rows:
category_id|attribute_id
1|1
1|2
1|3
I want to have the query which suites the two following needs. I have a variable (php) of allowed attribute_id's. If the array is subset of attribute_id then category_id should be selected, if not - no results.
First case:
select * from category_attributes where (1,2,3,4) in category_attributes.attribute_id
should give no results.
Second case
select * from category_attributes where (1,2,3) in category_attributes.attribute_id
should give all three rows (see dummy rows at the beginning).
So I would like to have reverse side of what standard SQL in does.
Solution
Step 1: Group the data by the field you want to check.
Step 2: Left join the list of required values with the records obtained in the previous step.
Step 3: Now we have a list with required values and corresponding values from the table. The second column will be equal to required value if it exist in the table and NULL otherwise.
Count null values in the right column. If it is equal to 0, then it means table contains all the required values. In that case return all records from the table. Otherwise there must be at least one required value is missing in the table. So, return no records.
Sample
Table "Data":
Required values:
10, 20, 50
Query:
SELECT *
FROM Data
WHERE (SELECT Count(*)
FROM (SELECT D.value
FROM (SELECT 10 AS value
UNION
SELECT 20 AS value
UNION
SELECT 50 AS value) T
LEFT JOIN (SELECT value
FROM Data
GROUP BY value) D
ON ( T.value = D.value )) J
WHERE value IS NULL) = 0;
You can use group by and having:
select ca.category_id
from category_attributes ca
where ca.attribute_id in (1, 2, 3, 4)
group by ca.category_id
having count(*) = 4; -- "4" is the size of the list
This assumes that the table has no duplicates (which is typical for attribute mapping tables). If that is a possibility, use:
having count(distinct ca.attribute_id) = 4
You can aggregate attribute_id into array and compare two array from php.
SELECT category_id FROM
(select category_id, group_concat(attribute_id) as attributes from category_attributes
order by attribute_id) t WHERE t.attributes = (1, 2, 3);
But you need to find another way to compare arrays or make sure that array is always sorted.

MYSQL The used SELECT statements have a different number of columns

I need to combined 2 tables with the same ids in it but i can't
SELECT stat.user_id, user.username,
SUM(stat.vertrag) AS vertrag,
SUM(stat.zubehoer) AS zubehoer,
SUM(stat.privat) AS privat,
SUM(stat.service) AS service,
SUM(stat.bs_vertrag) AS bus
FROM statistics stat
join users user on stat.user_id = user.uid
WHERE stat.user_id != '0' AND stat.datum LIKE '%$month%'
GROUP BY stat.user_id
UNION
SELECT bew.user_id, stat.user_id, user.username,
SUM(case when bew.log = 'inv_imei'
THEN
1
ELSE
0
END) AS inv
FROM user_bewegungen bew
JOIN users user ON user.uid = bew.user_id
JOIN statistics stat ON bew.user_id = stat.user_id
WHERE bew.date LIKE '%$month%'
GROUP BY bew.user_id
ORDER BY vertrag DESC
I am dont know how to go now.....
The first select is perfect, and works. now i have add a union because i need to add the row "log". Id's are also in it but i become the error
The used SELECT statements have a different number of columns
Can anyone help?
Each select statement needs to have the same number of columns. Your first one has 7:
SELECT
stat.user_id,
user.username,
SUM(stat.vertrag) AS vertrag,
SUM(stat.zubehoer) AS zubehoer,
SUM(stat.privat) AS privat,
SUM(stat.service) AS service,
SUM(stat.bs_vertrag) AS bus
Your second one has 4:
SELECT
bew.user_id,
stat.user_id,
user.username,
SUM(case when bew.log = 'inv_imei' THEN 1 ELSE 0 END) AS inv
You can select NULL in the second SELECT for those columns that aren't in the first one.
Make the two operands of the UNION isomorphic. Rename columns and/or create NULL-valued dummy columns as necessary to give them the same shape. FOR EXAMPLE, if we wanted to form the UNION of:
SELECT a, b, c
FROM table1
and:
SELECT d, e
FROM table2
we would logically pair those columns that are of the same types (in this case, let's assume that a and e are of the same type, and that b and d are of the same type) and add an extra NULL-valued column as the third projected attribute of the right-hand SELECT, as follows:
SELECT b, a, c
FROM table1
UNION
SELECT d AS b, e AS a, NULL as c
FROM table2
If such an approach seems confusing, you can use table views to simplify the expression. In the preceding example, you could have asserted a view atop table2:
CREATE VIEW t2view( b, a, c )
AS
SELECT d, e, NULL
FROM table2
and then formulated your UNION as:
SELECT b, a, c
FROM table1
UNION
SELECT *
FROM t2view
In UNION, the field numbers should be the same. Use like this:
SELECT stat.user_id, 0, user.username, ....
SELECT bew.user_id, stat.user_id, user.username, ...
or use something else, what you know, that is a missing field there.
The data types should be the same also.
You are using MySQL Union.
UNION is used to combine the result from multiple SELECT statements into a single result set.
The column names from the first SELECT statement are used as the column names for the results returned. Selected columns listed in corresponding positions of each SELECT statement should have the same data type. (For example, the first column selected by the first statement should have the same type as the first column selected by the other statements.)
Reference: MySQL Union
Your first select statement has 7 columns and second statement has 4.
You should have same number of column and also in same order in both statement.
otherwise it shows error or wrong data.
you can see this example
there are two queries both queries have the same number of columns.
column name can be different.
select 'row1' as column1,'row2' as column2
union
select 'row3' as column11,'row4' as column222
if you change columns count, it means in first query you are selecting 2 columns and in second query you are using 3 columns then it will through an error (The used SELECT statements have a different number of columns).
select 'row1' as column1,'row2' as column2
union
select 'row3' as column11,'row4' as column222 ,'rr' as t ;
run both queries you will see differnce.

mysql query return null row [duplicate]

This question already has answers here:
SELECT SUM returns a row when there are no records
(8 answers)
Closed 7 years ago.
i want to execute this query but it return null row when the table empty. it's working when SUM(products.sale_price)/COUNT(orders.id) AS avg_price_rang
is not in the query
SELECT
products.brand_id,
(SELECT
category_brands.name
FROM
category_brands
WHERE
category_brands.id=products.brand_id
) AS brand,
products.material_id,
(SELECT
category_materials.material
FROM
category_materials
WHERE
category_materials.id=products.material_id
) AS material,
orders.color_code,
SUM(products.sale_price)/COUNT(orders.id) AS avg_price_rang
FROM
orders
INNER JOIN
products
ON
orders.prodcut_id = products.id
I think it's missing GROUP BY products.brand_id.
SELECTing SUM when there are no rows causess a row of all NULLs to be returned.
MySQL 5.5 Reference Manual
12.17.1 GROUP BY (Aggregate) Functions
If you use a group function in a statement containing no GROUP BY clause, it is equivalent to grouping on all rows. For more information, see Section 12.17.3, “MySQL Handling of GROUP BY”.
Use:
query
HAVING avg_price_rang IS NOT NULL
SELECT * FROM (
query
) dummy
WHERE avg_price_rang IS NOT NULL