I have been trying to obtain the latest serial number of a particular product, so that I can show the next available serial number in my admin area.
Following is what I have been trying
SQL Fiddle
MySQL 5.5.32 Schema Setup:
CREATE TABLE IF NOT EXISTS `serials` (
`sn` varchar(11) NOT NULL,
`cxid` varchar(11) DEFAULT NULL,
`itmid` varchar(11) DEFAULT NULL,
PRIMARY KEY (`sn`)
);
INSERT INTO `serials` (`sn`, `cxid`, `itmid`) VALUES
('7', '00007', 'Name'),
('8', '00008', 'Name'),
('9', '00010', 'Name'),
('10', '00010', 'Name'),
('11', '00010', 'Name'),
('12', '00012', 'Name'),
('13', '00013', 'Name');
Query 1:
SELECT
sn
FROM serials AS t
INNER JOIN (SELECT MAX(sn) AS one FROM serials where cxid = '00010') AS s ON s.one = t.sn
Results:
I always get an empty result no matter what I do. What might be the problem? Maybe there is a much easier way?
And the point to note is that I have to get the serial only of a particular product, NOT from the entire table.
Are you using the right field? itmid = 0010? Shouldn't it be cxid?
You also have no values matching 0010. You should use '00010'. I'm fairly certain 0010 will not equal 00010 when you are using a varchar data type and you should wrap it in quotes to evaluate it as a string.
Lastly, sn from the looks of it should be an integer type. Without it being an integer, MAX() won't work correctly. There is a workaround for this if you are certain you need it as a varchar you can use ABS:
SELECT MAX(ABS(sn)) AS one FROM serials where cxid = '00010'
Are you using wrong field.
Use this :
SELECT sn FROM serials AS t INNER JOIN (SELECT MAX(sn) AS one FROM serials where cxid = '00010') AS s ON s.one = t.sn
You should use '00010' instead of '0010'. because you have no values matching 0010.
Related
I had a statement in PSQL that did this just fine (based on an answer from this thread)
But I've been trying to recreate the same statement in MySQL which has been difficult. My current workaround is ugly:
CREATE TEMPORARY TABLE potential_duplicates (
id VARCHAR(64)
content TEXT,
) CHARACTER SET utf8mb4 COLLATE utf8mb4_bin;
INSERT INTO potential_duplicates(id, content) VALUES ('1', 'some content'), ('2', 'some more content');
SELECT id, content FROM potential_duplicates WHERE id IN (SELECT id FROM main_table);
INSERT INTO main_table(id, content)
SELECT id, content FROM potential_duplicates WHERE id NOT IN (SELECT id FROM main_table);
This query is going to be used very often and I feel like making a temporary table every query is inefficient. Originally I was trying to find a way to use the INSERT ON DUPLICATE feature. It would have worked fine if I was just inserting and nothing else, but i need to return the duplicate values in the end.
Assuming id is a primary key (or at least unique), you can use INSERT IGNORE to avoid duplicates and RETURNING (available as of MariaDB 10.5) to return the inserted id values:
INSERT IGNORE main_content(id, content)
VALUES ('2', 'some content'), ('5', 'some more content')
RETURNING id
Demo on dbfiddle
So this is likely something simple, but I'm pulling my hair out trying to figure out an efficient way of doing this. I've looked at many other Q&A's, and I've messed with DISTINCT, GROUP BY, sub-queries, etc.
I've tried to super-simplify this example. (for the purpose of the example, there's no DB normalization) Here's a SQL fiddle:
http://sqlfiddle.com/#!9/948be7c/1
CREATE TABLE IF NOT EXISTS `orders` (
`id` int NOT NULL,
`name` varchar(90) NULL,
`email` varchar(200) NULL,
`phone` varchar(200) NULL,
PRIMARY KEY (`id`)
) DEFAULT CHARSET=utf8;
INSERT INTO `orders` (`id`, `name`, `email`, `phone`) VALUES
('1', 'Bob', 'bob#email.com', NULL),
('2', 'Bobby', 'bob#email.com', '1115551111'),
('3', 'Robert', 'robert#email.com', '1115551111'),
('4', 'Fred', 'fred#email.com', '1115552222'),
('5', 'Freddy', 'fred#email.com', '1115553333')
If I just run a simple select, I'll get:
But I'd like to "de-duplicate" any results that have the same email address or that have the same phone number - because they will be the same people, even if there are multiple ID's for them, and even if their names are spelled different. And then consolidate those results (one of the "distinct" email addresses and one of the "distinct" phone numbers along with one of the names and one of the ID's.)
So that for the above, I'd end up with something like this:
Any suggestions?
I think that you can do what you want by filtering with a correlated subquery:
select o.*
from orders o
where o.id = (
select o1.id
from orders o1
where o1.email = o.email or o1.phone = o.phone
order by o1.phone is not null desc, o1.email is not null desc, id
limit 1
)
This retains just one row out of those that have the same phone or email, while giving priority to the row whose phone and email is not null. Ties are broken by picking the lowest id.
For your sample data, this returns:
id name email phone
2 Bobby bob#email.com 1115551111
4 Fred fred#email.com 1115552222
There are a number of different ways your requirements could be interpreted.
One way would be to reframe it as a constraint: only return a record if one of these is true:
it has a non-null email and phone, and no record exists with the same email and phone and a lower id
it has a non-null email but null phone, and no record exists with the same email and a non-null phone, and no record exists with the same email and a null phone and a lower id
it has a non-null phone but null email, and no record exists with the same phone and a non-null email, and no record exists with the same phone and a null email and a lower id
This translates easily into a couple of joins, no group by or distinct required.
I have a table that contains a bunch of numbers seperated by a comma.
I would like to retrieve rows from table where an exact number not a partial number is within the string.
EXAMPLE:
CREATE TABLE IF NOT EXISTS `teams` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`name` varchar(255) NOT NULL,
`uids` text NOT NULL,
`islive` tinyint(1) NOT NULL DEFAULT '1',
PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8 AUTO_INCREMENT=5 ;
INSERT INTO `teams` (`id`, `name`, `uids`, `islive`) VALUES
(1, 'Test Team', '1,2,8', 1),
(3, 'Test Team 2', '14,18,19', 1),
(4, 'Another Team', '1,8,20,23', 1);
I would like to search where 1 is within the string.
At present if I use Contains or LIKE it brings back all rows with 1, but 18, 19 etc is not 1 but does have 1 within it.
I have setup a sqlfiddle here
Do I need to do a regex?
You only need 1 condition:
select *
from teams
where concat(',', uids, ',') like '%,1,%'
I would search for all four possible locations of the ID you are searching for:
As the only element of the list.
As the first element of the list.
As the last element of the list.
As an inner element of the list.
The query would look like:
select *
from teams
where uids = '1' -- only
or uids like '1,%' -- first
or uids like '%,1' -- last
or uids like '%,1,%' -- inner
You could probably catch them all with a OR
SELECT ...
WHERE uids LIKE '1,%'
OR uids LIKE '%,1'
OR uids LIKE '%, 1'
OR uids LIKE '%,1,%'
OR uids = '1'
You didn't specify which version of SQL Server you're using, but if you're using 2016+ you have access to the STRING_SPLIT function which you can use in this case. Here is an example:
CREATE TABLE #T
(
id int,
string varchar(20)
)
INSERT INTO #T
SELECT 1, '1,2,8' UNION
SELECT 2, '14,18,19' UNION
SELECT 3, '1,8,20,23'
SELECT * FROM #T
CROSS APPLY string_split(string, ',')
WHERE value = 1
You SQL Fiddle is using MySQL and your syntax is consistent with MySQL. There is a built-in function to use:
select t.*
from teams t
where find_in_set(1, uids) > 0;
Having said that, FIX YOUR DATA MODEL SO YOU ARE NOT STORING LISTS IN A SINGLE COLUMN. Sorry that came out so loudly, it is just an important principle of database design.
You should have a table called teamUsers with one row per team and per user on that team. There are numerous reasons why your method of storing the data is bad:
Numbers should be stored as numbers, not strings.
Columns should contain a single value.
Foreign key relationships should be properly declared.
SQL (in general) has lousy string handling functions.
The resulting queries cannot be optimized.
Simple things like listing the uids in order or removing duplicate are unnecessarily hard.
I have an ecommerce site wherby customers login and see prices for various products. Each customer has their own pricelist and the the prices can be set a number of ways (sources). The table is below:
CREATE TABLE customer_price_list (
customer_price_id int(11) NOT NULL AUTO_INCREMENT,
customer_account_id int(11) NOT NULL,
product_id int(11) DEFAULT NULL,
currency_id int(11) DEFAULT NULL,
customer_price decimal(7,2) DEFAULT NULL,
source enum('TRADE_DEFAULT','TEMPLATE','SAGE','MANUAL') DEFAULT NULL,
PRIMARY KEY (customer_price_id),
KEY customer_account_id (customer_account_id),
KEY product_id (product_id),
KEY currency_id (currency_id),
KEY source (source)
)
Taking just the product_id, customer_price and source, sample data for one customer may look like (using a string for product_id just for illustration) :
Prod_1, 10.00, TRADE_DEFAULT
Prod_2, 20.00, TRADE_DEFAULT
Prod_3, 25.00, MANUAL
And a different customer:
Prod_1, 7.50, SAGE
Prod_2, 20.00, TRADE_DEFAULT
Prod_3, 30.00, TRADE_DEFAULT
The basic price for something, without a discount, is when the source is TRADE_DEFAULT - the above shows these two customers had the TRADE_DEFAULT price for two items each but got a discount on one item.
The TRADE_DEFAULT prices are set by importing a CSV file which has product_id, currency_id and customer_price in it. Within PHP I loop through all the rows in the CSV and bind the values to this query:
insert into customer_price_list
(customer_price_id, customer_account_id, product_id, currency_id, customer_price, source)
select 0, customer_account_id, :product_id, :currency_id, :price, 'TRADE_DEFAULT' from customer_account
This works fine when customer_price_list is empty (for all product_id/currency_id combinations within the CSV). But, if some customers already have product_id/currency_id entries this will result in extra rows (i.e. there will be two or more prices for a product for that customer)
So, when processing the CSV I want to:
A) update any existing TRADE_DEFAULT to the new value from the CSV (again run from a loop of the CSV contents)
update customer_price_list set customer_price = :price
where currency_id=:currency_id and product_id=:product_id and source ='TRADE_DEFAULT'
B) insert a TRADE_DEFAULT price if that customer has no price for that product/currency
insert (ONLY IF NO PRICE OF ANY SOURCE IS THERE) into customer_price_list
(customer_price_id, customer_account_id, product_id, currency_id, customer_price, source)
select 0, customer_account_id, :product_id, :currency_id, :price, 'TRADE_DEFAULT' from customer_account
This is where I need help. I have searched for conditional insert queries but I can only find where they are inserting one recoord like this:
insert into table1 (user,rating,last_modified)
select 'user1', 1999, NOW() from table1
where not exists (
select * from table1
where last_modified > '2007-04-13 08:52:41'
and user='user1'
) limit 1
But I am wanting to do insert .. select and do an inseret, if needed, for all customers.
Thanks.
Try using merge:
https://learn.microsoft.com/en-us/sql/t-sql/statements/merge-transact-sql
It may be helpful with your case as it allows to insert values based on condition and update if they already exist.
Using #Stavr00 suggestion I came up with this:
First add an index across three columns to make product/currency unique for each customer:
alter table customer_price_list add UNIQUE INDEX unq_cust_prod (customer_account_id ASC, product_id ASC, currency_id ASC)
Then change the query:
insert into customer_price_list
(customer_price_id, customer_account_id, product_id, currency_id, customer_price, source)
select 0, customer_account_id, :product_id, :currency_id, :price, 'TRADE_DEFAULT' from customer_account
ON DUPLICATE KEY UPDATE customer_price=customer_price;
By making the update set the price to the current value, the query becomes just the conditional insert. When combined with the update query in step A in my question, it does what I want.
I guess I could have used the ON DUPLICATE KEY UPDATE to do the work of the other query and do it all on one. But, I would only want to update existing records of source = TRADE_DEFAULT so not sure how to do that.
Also, it seems I could have used insert ignore, but as this would not flag some real errors like type mismatch, I thought my solution was safer.
Comments welcome!
I have a SQL query, it looks like that:
insert into tableA(order_id, contractnotenumber, shipmentdate, comment)
values ((select entity_id from tableb where increment_id = '12345678'),
'', '1970-01-01', '');
Now, if the subquery ((select entity_id from tableb where increment_id = '12345678')) returns null, the insert does not work. What I want is an easy way to say that if the subquery returns null, dont do anything. I tried insert ignore, but this inserts the row with 0 instead of null, which works, but its not what I want. In SQL Server I used if not exists(select...), but this does not work with MYSQL.
Ideas?
Thanks!
insert into tableA(order_id, contractnotenumber, shipmentdate, comment)
select entity_id, '', '1970-01-01', '' from tableb where increment_id = '12345678'
This won't insert anything if the row isn't found in tableb
You could also try mysql> insert ignore