SQL unwanted results in NOT query - mysql

This looks like it should be really easy question, but I've been looking for an answer for the past two days and can't find it. Please help!
I have two tables along the lines of
texts.text_id, texts.other_stuff...
pairs.pair_id, pairs.textA, pairs.textB
The second table defines pairs of entries from the first table.
What I need is the reverse of an ordinary LEFT JOIN query like:
SELECT texts.text_id
FROM texts
LEFT JOIN text_pairs
ON texts.text_id = text_pairs.textA
WHERE text_pairs.textB = 123
ORDER BY texts.text_id
How do I get exclusively the texts that are not paired with A given textB? I've tried
WHERE text_pairs.textB != 123 OR WHERE text_pairs.textB IS NULL
However, this returns all the pairs where textB is not 123. So, in a situation like
textA TextB
1 3
1 4
2 4
if I ask for textB != 3, the query returns 1 and 2. I need something that will just give me 1.

The comparison on the second table goes in the ON clause. Then you add a condition to see if there is no match:
SELECT t.text_id
FROM texts t LEFT JOIN
text_pairs tp
ON t.text_id = tp.textA AND tp.textB = 123
WHERE tp.textB IS NULL
ORDER BY t.text_id ;
This logic is often expressed using NOT EXISTS or NOT IN:
select t.*
from texts t
where not exists (select 1
from text_pairs tp
where t.text_id = tp.textA AND tp.textB = 123
);

Related

Joining 2 tables that use LIKE as a common identifier

I have two tables.
wp_rg_lead_detail:
id lead_id form_id field_number value
=====================================================
166649 2579 4 235 batman
167324 2602 4 235 batman
168439 2579 4 235 kelsey
169221 2836 4 235 batman
wp_rg_incomplete_submissions:
uuid form_id submission
=======================================================================
fds4389dsd2kjd 4 JSON entry that doesn't contain 'kelsey
ciwod2938slsck 4 JSON entry that contains 'kelsey
392copaa234jfl 4 JSON entry that doesn't contain 'kelsey
What I want to do is grab the record that:
has the word 'kelsey' in wp_rg_incomplete_submissions.submission
has a wp_rg_incomplete_submissions.form_id of 4
has the word 'kelsey' as a value in wp_rg_lead_detail
and the lead_id for that entry in wp_rg_lead_details should also have the word 'batman' for a value.
The only identifier between the two tables is the word 'kelsey'. But where it exists in wp_rg_lead_detail, that lead_id must also have an entry with the value of 'batman'.
I have tried subqueries and joins, and I'm getting nowhere. Can someone please point me in the right direction?
UPDATE
From the feedback below, it sounds like I should create an alias and then join them where that exists in both. Here's where I'm at:
SELECT *, 'kelsey' AS myvalue
FROM `wp_rg_lead_detail`
WHERE (`value` LIKE 'batman'
OR `value` LIKE 'kelsey')
AND `form_id` = 4
GROUP BY `lead_id`
HAVING count(*) > 1
I think somehow I need to join this where the LIKE uses myvalue:
SELECT *, uuid
FROM `wp_rg_incomplete_submissions`
WHERE `form_id` = 4
AND `submission` LIKE concat_ws(";", "%", myvalue, "%")
UPDATE #2
After continuing to struggle with this, I've come up with:
SELECT *
FROM wp_rg_lead_detail
INNER JOIN wp_rg_incomplete_submissions ON wp_rg_lead_detail.value
LIKE CONCAT('%', wp_rg_incomplete_submissions.submission, '%')
WHERE wp_rg_lead_detail.value = 'kelsey'
I know I'm doing something wrong because there are no results. But I feel it is much closer than where I started from.
So here is what I came up with, not vouching for it's efficiency as I don't write much SQL.
SELECT *
FROM submissions
JOIN (SELECT detail.*
FROM detail
JOIN detail detail2
ON detail2.lead_id = detail.lead_id
WHERE detail.value = 'kelsey'
AND detail2.value = 'batman'
) as detailjoin
ON detailjoin.form_id = submissions.form_id
WHERE submissions.submission LIKE '%kelsey%'
AND submissions.form_id = 4;
Which from you data set returns:
'ciwod2938slsck' 4 'JSON with kelsey' 168439 2579 4 235 'kelsey'
So to break it down, the inner join query gets all detail rows that have 'kelsey' as a value where that lead_id also exists in a row with a 'batman' value.
The outer query selects all rows with form_id of 4 and 'kelsey' in submission
Then it simply joins the two on form_id = form_id.
I believe this does what you needed although with the small data set not positive.

MySQL Query with LEFT JOIN where second table has a 2-Part Primary Key

I have 2 tables in a MySQL database (storeskus). The first is FBM_Orders and the second is IM_INV.
I am trying the query
SELECT `FBM_Orders`.`order-id`,`FBM_Orders`.`order-item-id`,`FBM_Orders`.`purchase-date`,
`FBM_Orders`.`promise-date`,`FBM_Orders`.`buyer-name`,`FBM_Orders`.`sku`,
`FBM_Orders`.`product-name`,`FBM_Orders`.`quantity-purchased`,
`FBM_Orders`.`recipient-name`,`IM_INV`.`LOC_ID`,`IM_INV`.`QTY_ON_HND`
FROM `FBM_Orders`
LEFT JOIN `IM_INV` ON `FBM_Orders`.`sku` = `IM_INV`.`ITEM_NO`
WHERE `FBM_Orders`.`quantity-to-ship` > 0
ORDER BY `FBM_Orders`.`purchase-date`, `IM_INV`.`LOC_ID` ASC;
Because the IM_INV table has a 2-part primary key: ITEM_NO & LOC_ID, I am getting 4 lines for each ITEM_NO with the QTY_ON_HND for each of the 4 locations (LOC_ID).
I am fairly new to SQL so I'm thrilled to have gotten this far, but how can I make it so that the result is a single line per ITEM_NO but with a column for each LOC_ID with its QTY_ON_HND?
Example:
My current result is
FBM_Order.sku FBM_Order.quantity-purchased IM_INV.LOC_ID QTY_ON_HND
'SCHO645256' 1 AF 2
'SCHO645256' 1 LO 2
'SCHO645256' 1 S 3
'SCHO645256' 1 SL 1
How can I change that to
FBM_Order.sku FBM_Order.quantity-purchased QTY_ON_HND_AF QTY_ON_HND_LO QTY_ON_HND_S QTY_ON_HND_SL
'SCHO645256' 1 2 2 3 1
?
Thanks!
You may load it as you already do and treat it inside your application, but if you really wanna make that inside your MySQL, try GROUP CONCAT and JSON as follows:
SELECT
GROUP_CONCAT(JSON_OBJECT(
'LOC_ID', IM_INV.LOC_ID,
'QTY_ON_HND', QTY_ON_HND
))
{another fields}
FROM `FBM_Orders`
LEFT JOIN `IM_INV` ON `FBM_Orders`.`sku` = `IM_INV`.`ITEM_NO`
WHERE `FBM_Orders`.`quantity-to-ship` > 0
GROUP BY `FBM_Orders`.`order-id`;
Note: JSON is just available for MySQL 5.7+ and may slow down your query a little bit. You're still gonna need convert your data to array inside your application. So it's half done inside your app and half inside your database.

MySQL take duplicate data and combine unique data

With my MySQL database, I want to take data from my temporary table and insert it into my main table, while removing any duplicate data but also taking into consideration the data I already have. This seems to require an update and/or an insert depending on what exists in "data_table" so I really have no idea how to write it or if it is even possible. If this isn't possible, I'd like to know how to accomplish this while not considering what is already in "data_table" which I would think is possible. Thank you for your help!
Existing data_table before running query:
data_table
+-----id-----+-----age-----+-----gender-----+-----color-----+
=============+==============+=================+================+
1 5 m pink,red,purple
data_table_temp
+-----id-----+-----age-----+-----gender-----+-----color-----+
=============+==============+=================+================+
1 5 m red
2 5 m blue
3 5 m red
4 5 m orange
5 6 m red
6 6 m green
7 6 m blue
After query:
data_table
+-----id-----+-----age-----+-----gender-----+-----color-----+
=============+==============+=================+================+
1 5 m pink,red,purple,blue,orange
2 6 m red,green,blue
Here is an approach to this problem which turned out to be harder than I expected.
The idea is to concat the colors that don't match and put them together. There is a bit of a problem assigning ids. Getting the "2" for the second row is a problem, so this just assigned the id sequentially:
select #id := #id + 1 as id,
coalesce(dt.age, dtt.age) as age,
coalesce(dt.gender, dtt.gender) as age,
concat_ws(dt.color,
group_concat(case when find_in_set(dtt.color, dt.color) > 0
then dtt.color
end)
)
from data_table_temp dtt left outer join
data_table dt join
on dt.age = dtt.age and
dt.gender = dtt.gender cross join
(select #id := 0) var
group by coalesce(dt.age, dtt.age), coalesce(dt.gender, dtt.gender);
MySQL doesn't have any string functions to (easily) split a delimited string (like data_table.color).
However, if you have all of the data in data_table_temp's format (one color per row), you can generate the desired results like this:
SELECT DISTINCT age, GROUP_CONCAT(DISTINCT color)
FROM table WHERE [condition]
GROUP BY age;
Optionally adding in gender, as necessary.
Apologies for the half-answer

MySQL Table UNION ISSUE

I'm trying to unite two tables in MySQL, the query I'm using is:
SELECT qa_invoicesitems.item_code, qa_invoicesitems.item_unitprice, qa_invoicesitems.item_subtotal, qa_invoicesitems.item_discount,
qa_invoicesitems.item_total
FROM qa_invoicesitems
RIGHT JOIN qa_invoicesitems_returnlog USING (item_code)
WHERE invoice_code = 17
UNION
SELECT qa_invoicesitems_returnlog.item_code, qa_invoicesitems_returnlog.item_unitprice, qa_invoicesitems_returnlog.item_subtotal,
qa_invoicesitems_returnlog.item_discount, qa_invoicesitems_returnlog.item_total
FROM qa_invoicesitems_returnlog
LEFT JOIN qa_invoicesitems USING (item_code)
WHERE returnlog_code = 9
But I can not behold the desired result.
A graphical example ..
Anyone have any idea how I can get this result?
NOTE: qa_invoicesitems_returnlog Replaces the data.
The codes: 1234, 1585, 23 are taken from (qa_invoicesitems_returnlog), because they exist in both tables, the rest are simply displayed.
Based upon your comments & your image, I'm going to say you want:
all the rows in table 2 where qa_invoicesitems_returnlog = 9
all the rows in table 1 where invoice_code = 17, except for those rows w/ item_id's that are already present in the output from table 2.
So, writing that out as a union, give you:
SELECT qa_invoicesitems_returnlog.item_code as item_code,
qa_invoicesitems_returnlog.item_unitprice as item_unitprice,
qa_invoicesitems_returnlog.item_subtotal as item_subtotal,
qa_invoicesitems_returnlog.item_discount as item_discount,
qa_invoicesitems_returnlog.item_total as item_total
FROM qa_invoicesitems_returnlog
WHERE qa_invoicesitems_returnlog.returnlog_code = 9
UNION
SELECT qa_invoicesitems.item_code as item_code,
qa_invoicesitems.item_unitprice as item_unitprice,
qa_invoicesitems.item_subtotal as item_subtotal,
qa_invoicesitems.item_discount as item_discount,
qa_invoicesitems.item_total as item_total
FROM qa_invoicesitems
WHERE qa_invoicesitems.invoice_code = 17
AND NOT EXISTS (SELECT * FROM qa_invoicesitems_returnlog qir2
WHERE qir2.returnlog_code = 9
and qir2.item_code = qa_invoicesitems.item_code)
The key is the NOT EXISTS clause that excludes from the results from table 1 (qa_invoiceitems) that are already present in the output from table 2 and have a matching item_code.
Why are you using a UNION? I think the following is equivalent to your query:
SELECT qa_invoicesitems.item_code, qa_invoicesitems.item_unitprice,
qa_invoicesitems.item_subtotal, qa_invoicesitems.item_discount,
qa_invoicesitems.item_total
FROM qa_invoicesitems RIGHT JOIN
qa_invoicesitems_returnlog
USING (item_code)
WHERE invoice_code = 15 or returnlogcode = 8
The LEFT and RIGHT joins are doing the same thing because the tables are reversed.

Combine Multiple child rows into one row MYSQL

I have two tables
Ordered_Item
ID | Item_Name
1 | Pizza
2 | Stromboli
Ordered_Options
Ordered_Item_ID | Option_Number | Value
1 43 Pepperoni
1 44 Extra Cheese
2 44 Extra Cheese
What I am looking to output is a mysql query is something to this effect
Output
ID | Item_Name | Option_1 | Option_2
1 Pizza Pepperoni Extra Cheese
2 Stromboli NULL Extra Cheese
I have tried numerous options most ending in syntax error, I have tried group_concat but thats not really what I am looking for. I have a crude example below of what I think might be a start. I need the options to be in the same order every time. And in the program where the info is collected there is no way to reliable ensure that will happen. Is it possible to have them concatenate according to option number. Also I know that I will never have over 5 options so a static solution would work
Select Ordered_Items.ID,
Ordered_Items.Item_Name,
FROM Ordered_Items
JOIN (SELECT Ordered_Options.Value FROM Ordered_Options Where Option_Number = 43) as Option_1
ON Ordered_Options.Ordered_Item_ID = Ordered_Item.ID
JOIN (SELECT Ordered_Options.Value FROM Ordered_Options Where Option_Number = 44) as Option_2
ON Ordered_Options.Ordered_Item_ID = Ordered_Item.ID;
The easiest way would be to make use of the GROUP_CONCAT group function here..
select
ordered_item.id as `Id`,
ordered_item.Item_Name as `ItemName`,
GROUP_CONCAT(Ordered_Options.Value) as `Options`
from
ordered_item,
ordered_options
where
ordered_item.id=ordered_options.ordered_item_id
group by
ordered_item.id
Which would output:
Id ItemName Options
1 Pizza Pepperoni,Extra Cheese
2 Stromboli Extra Cheese
That way you can have as many options as you want without having to modify your query.
Ah, if you see your results getting cropped, you can increase the size limit of GROUP_CONCAT like this:
SET SESSION group_concat_max_len = 8192;
I appreciate the help, I do think I have found a solution if someone would comment on the effectiveness I would appreciate it. Essentially what I did is. I realize it is somewhat static in its implementation but I does what I need it to do (forgive incorrect syntax)
SELECT
ordered_item.id as `Id`,
ordered_item.Item_Name as `ItemName`,
Options1.Value
Options2.Value
FROM ORDERED_ITEMS
LEFT JOIN (Ordered_Options as Options1)
ON (Options1.Ordered_Item.ID = Ordered_Options.Ordered_Item_ID
AND Options1.Option_Number = 43)
LEFT JOIN (Ordered_Options as Options2)
ON (Options2.Ordered_Item.ID = Ordered_Options.Ordered_Item_ID
AND Options2.Option_Number = 44);
If you really need multiple columns in your result, and the amount of options is limited, you can even do this:
select
ordered_item.id as `Id`,
ordered_item.Item_Name as `ItemName`,
if(ordered_options.id=1,Ordered_Options.Value,null) as `Option1`,
if(ordered_options.id=2,Ordered_Options.Value,null) as `Option2`,
if(ordered_options.id=43,Ordered_Options.Value,null) as `Option43`,
if(ordered_options.id=44,Ordered_Options.Value,null) as `Option44`,
GROUP_CONCAT(if(ordered_options.id not in (1,2,43,44),Ordered_Options.Value,null)) as `OtherOptions`
from
ordered_item,
ordered_options
where
ordered_item.id=ordered_options.ordered_item_id
group by
ordered_item.id
If you know you're going to have a limited number of max options then I would try this (example for max of 4 options per order):
Select OI.ID, OI.Item_Name, OO1.Value, OO2.Value, OO3.Value, OO4.Value
FROM Ordered_Items OI
LEFT JOIN Ordered_Options OO1 ON OO1.Ordered_Item_ID = OI.ID
LEFT JOIN Ordered_Options OO2 ON OO2.Ordered_Item_ID = OI.ID AND OO2.ID != OO1.ID
LEFT JOIN Ordered_Options OO3 ON OO3.Ordered_Item_ID = OI.ID AND OO3.ID != OO1.ID AND OO3.ID != OO2.ID
LEFT JOIN Ordered_Options OO4 ON OO4.Ordered_Item_ID = OI.ID AND OO4.ID != OO1.ID AND OO4.ID != OO2.ID AND OO4.ID != OO3.ID
GROUP BY OI.ID, OI.Item_Name
The group by condition gets rid of all of the duplicates that you would otherwise get. I've just implemented something similar on a site I'm working on where I knew I'd always have 1 or 2 matched in my child table, and I wanted to make sure I only had 1 row for each parent item.
What you want is called a pivot, and it's not directly supported in MySQL, check this answer out for the options you've got:
How to pivot a MySQL entity-attribute-value schema
Here is how you would construct your query for this type of requirement.
select ID,Item_Name,max(Flavor) as Flavor,max(Extra_Cheese) as Extra_Cheese
from (select i.*,
case when o.Option_Number=43 then o.value else null end as Flavor,
case when o.Option_Number=44 then o.value else null end as Extra_Cheese
from Ordered_Item i,Ordered_Options o) a
group by ID,Item_Name;
You basically "case out" each column using case when, then select the max() for each of those columns using group by for each intended item.
Joe Edel's answer to himself is actually the right approach to resolve the pivot problem.
Basically the idea is to list out the columns in the base table firstly, and then any number of options.value from the joint option table. Just left join the same option table multiple times in order to get all the options.
What needs to be done by the programming language is to build this query dynamically according to a list of options needs to be queried.