Find Record if value does not exist? - mysql

I am having problem with the SQL Query.
I want to find StatusID = 1 in the records table IF StatusID = 2 does not exist.
I have tried the query:
SELECT * FROM records AS A
LEFT JOIN records AS B on B.StoreID = A.StoreID
WHERE A.StatusID = 1 AND B.StatusID != 2
It is still showing the result even if StatusID = 2 is exist.
Note: StoreID are the ref id in the records table.

You want to use NOT EXISTS:
SELECT *
FROM records AS A
WHERE A.StatusID = 1
AND NOT EXISTS (select B.StoreID
from records AS B
where B.StoreID = A.StoreIDB
and B.StatusID = 2)

Assumming non-null StatusID field (is always filled in):
SELECT * FROM records AS A
LEFT JOIN records AS B
on B.StoreID = A.StoreID
and B.StatusID = 2
WHERE A.StatusID = 1 AND B.StatusID IS NULL

You're still seeing the result even if StatusID = 2 exists because you're using a LEFT JOIN, which as you'll remember returns the entire left set and all matching entries from the right set or a NULL where none exist.
Seems to me the easiest fix is to just use an INNER JOIN, since for the cases LEFT JOIN will return that INNER JOIN won't, StatusID will equal neither 1 nor 2.
Thus:
select * from records a
inner join records b
on b.storeid = a.storeid
where a.statusid = 1
and b.statusid <> 2

Related

Join a joined table to itself

I have a database table, dictionary, that has two strings and a language id.
The columns are:
id
product_id
key
translation
language_id
Another table, dictionary_versions, just has the dictionary_id and version of translation
id
dictionary_id
version_id
I have two different translations in the table for each key, but sometimes, only one translation for the key. I am looking for a way to get all translations of one language, and, if there are any, translation of another language id.
Im looking to get all translation of one language_id and organizer_id and one version. I have tried to create a temporary table with all values of what I am comparing to, then doing a left join of the temporary table on the dictionary table to get all translated values of the desired language, and if there are any translations of another language, include those.
The problem is that it gets slow when there are 10000 translations for two languages. Is there a better way to join a table onto itself using the dictionary_versions table as a where clause on both?
SELECT
d.*
FROM
dictionary d
LEFT JOIN
dictionary_versions dv ON dv.dictionary_id = d.id
LEFT JOIN
dictionary d2
LEFT JOIN
dictionary_versions dv2 ON d2.id = dv2.dictionary_id
ON
d2.key = d.key
WHERE
d.product_id = 1 AND dv.version_id = 3
AND
d.language_id = 1
LIMIT
0,10
This was one of the queries I tried. However, if there are multiple versions, then it gets all version of the fd2 table, leading to data inaccuracies.
The other way I tried, was with the temporary table, this works, but it slow. it is done in two queries:
CREATE TEMPORARY TABLE IF NOT EXISTS dictionary_temp_1
AS (
SELECT d.* FROM `dictionary` AS `d`
LEFT JOIN `dictionary_versions` AS `dv` ON dv.dictionary_id = d.id
WHERE d.product_id = 1 AND dv.version_id = 3 AND d.language_id = 1
ORDER BY fd.key ASC LIMIT 0,10 )
Second:
SELECT
d.key,
d2.key AS toKey,
d.translation AS `1`,
d2.translation AS `2`
FROM
`dictionary` AS `d`
LEFT JOIN
`dictionary_versions` AS `dv` ON d.id = dv.dictionary_id
LEFT JOIN
`dictionary_temp_1` AS `d2` ON d2.key = d.key
WHERE
d.product_id = 1 AND dv.version_id = 3 AND d.language_id = 1
ORDER BY
d.key ASC LIMIT 0,10
I managed to find a solution using this as a reference, thanks to #philipxy, pointing me to this answer for reference.
SELECT d.key, d2.key AS toKey, d.translation AS `1`, d2.translation AS `2`
FROM dictionary d
INNER JOIN dictionary_versions dv ON dv.dictionary_id = d.id
LEFT JOIN (
SELECT d.*
FROM `dictionary` AS `d`
INNER JOIN `dictionary_versions` AS `dv` ON d.id = dv.dictionary_id
WHERE d.product_id = 1 AND dv.version_id = 3 AND d.language_id = 2
) d2 ON d2.key = d.key
WHERE d.product_id = 1 AND dv.version_id = 3 AND d.language_id = 1
This creates a subquery before joining it to the table itself. The where condition is applied to both tables, therefore getting the translations for one language_id, and matching any for a different language_id, if they exist.

Can't SELECT records from two tables properly

I've been using this Query at first, Then used this Query at last
Which are
##Query[1]
SELECT t.title, p.content, p.version
FROM drafts d
INNER JOIN titles t ON t.id = d.tp_id AND d.t = 1
INNER JOIN posts p ON p.id = d.tp_id AND d.t = 2
WHERE user_id = 1
##Query[2]
SELECT coalesce(p.title, t.title), p.content, p.version
FROM drafts d
INNER JOIN titles t ON t.id = d.tp_id AND d.t = 1
INNER JOIN
(
SELECT pt.id, tp.title, pt.content, pt.version
FROM posts pt
INNER JOIN titles tp ON tp.id = pt.tid
) p ON p.id = d.tp_id AND d.t = 2
WHERE user_id = 1
What I want to do Using the Table: drafts to use
Column: tp_id as an identifier to the Column: id in other tables.
Column: user_id as the id of the user having the draft.
Column: t as the identifier to which table of Tables: titles[t=1], posts[t=2] to fetch the records from it.
In Table: posts the Column: tid links to the Column: id of Table: titles Which is used to pull the Column: title.
I want no records to be shown from the Table: titles if doesn't exist, And no records to be shown from the Table: posts if the title related using tid on Table: titles doesn't exist Or the record of the Table: posts doesn't exist, That why I used INNER JOIN.
But in both queries I don't get any results at all.
Is this even the proper table scheme and design to be used for something like the Drafts table for two different tables instead of one for each table?
The result I expect is something like this
title content version
TheTitle null null
TheTitle Content1 1
TheTitle Content2 2
TheTitle Content3 3
TheTitle Content4 4
TitleThe Content1 1
TitleThe Content2 2
TitleThe Content3 3
TitleThe Content4 4
A simple Example:
[table-a] [table-b]
[id - title ] [id - table_a_id - content ]
[1 - title-1] [1 - 1 - content-1]
table-a has the titles.
table-b has the content of each
title.
table-b column of table_a_id links the content with
the title of table-a.
[table-ab]
[user_id - table_col_id - table_letter]
[1 - 1 - a ]
[1 - 1 - b ]
table-ab has user_id to tell which user will be shown the records.
table-ab has table_letter to tell the query which table to fetch the data from it.
What I want the Query to do is to check table-ab and fetch the data from the other tables according to it, so it would be
table_letter = a => fetching rows from table-a.
table_col_id = 1 => fetching rows from table-a WHERE id = 1.
result = title-1
table_letter = b => fetching rows from table-b.
table_col_id = 1 => fetching rows from table-b WHERE id = 1.
result = title-1 - content-1
Final result:
id - title - content
-------------------------
1 - title-1 - null
2 - title-1 - content-1
Do two separate queries (for t=1 and t=2) and use UNION ALL to combine the results:
SELECT t.title, null content , null version
FROM drafts d
INNER JOIN titles t
ON t.id = d.tp_id
WHERE d.user_id = 1 and d.t = 1
UNION ALL
SELECT pt.title, pt.content, pt.version
FROM drafts d
INNER JOIN
( SELECT p.id, t.title, p.content, p.version
FROM posts p
INNER JOIN titles t ON t.id = p.tid ) pt
ON pt.id = d.tp_id AND d.t = 2
WHERE d.user_id = 1
As you are using inner join so please check the following:
Common data in both tables(joining column), if there will not be any common value it will not pick any thing.
Check data type of the column which are used to join both table that should be same if not then handle it
Extra spaces in the joining column.
After joining with condition d.t = 1 there is no data for d.t = 2 That's why after 2nd Join you are not getting any data. Try the below Query
SELECT coalesce(p.title, t.title), p.content, p.version
FROM drafts d
INNER JOIN titles t
ON t.id = d.tp_id
INNER JOIN
(
SELECT pt.id, tp.title, pt.content, pt.version
FROM posts pt
INNER JOIN titles tp
ON tp.id = pt.tid
) p
ON p.id = d.tp_id
WHERE user_id = 1
AND (d.t = 1 OR d.t = 2)
This will produce almost exactly what you have in your question. It does omit the line with the null values for the content and version. Not sure why that would have been in the expected output.
SELECT
if(d.t = 1,tp.title, if(d.t = 2,t.title,"")) as `title`,
p.content,
p.version
FROM drafts d
LEFT JOIN titles t ON t.id = d.tp_id AND d.t = 2
LEFT JOIN posts p ON t.id = p.tid
LEFT JOIN titles tp ON tp.id = p.tid AND d.t = 1
WHERE user_id = 1 AND NOT p.content IS NULL
I changed some of the data, specifically the groups of Content1 - Content4. I added "a" to the first 4, and "b" to the remainder.
Here's a SQL Fiddle that shows what it does.

How can I accomplish the following in SQL?

I have two tables.
table_a:
id | data_x | data_y
--------------------
1 person joe
2 person bob
3 amount 200
4 addres philville
tableB:
map_id | table_a_id
-------------------
7 1
7 3
7 4
8 4
8 2
The result I want is the map_id if it has an entry in table_a for both data_x = 'person' and data_y = '200'
So with the above table B, the result should be
map_id
------
7
How can I write that query in SQL?
This situation is a perfect fit for an unusual SQL operator: INTERSECT. It is a very declarative, efficient and elegant solution for this problem.
SELECT Map.map_id
FROM Table_B AS Map JOIN Table_A AS Person ON (Person.id = Map.table_a_id) AND (Person.data_x = 'person')
INTERSECT
SELECT Map.map_id
FROM Table_B AS Map JOIN Table_A AS Amount ON (Amount.id = Map.table_a_id) AND (Amount.data_y = '200')
Formally what you are asking for is exactly the intersection of two disjoint sets: the set of map id's that are persons and the set of map id's that have a value of 200.
Please note the INTERSECT operator does not exists in MySQL, but it does in almost all advanced relational DBMS, including PostgreSQL.
This is less elegant than the INTERSECT solution #Malta posted, but it works with the limited capabilities of MySQL as well:
SELECT b1.map_id
FROM table_a a1
JOIN tableb b1 ON a1.id = b1.table_a_id AND a1.data_x = 'person'
JOIN tableb b2 ON b2.map_id = b1.map_id AND b2.table_a_id <> b1.table_a_id
JOIN table_a a2 ON a2.id = b2.table_a_id AND a2.data_y = '200';
SQL Fiddle for MySQL.
SQL Fiddle for Postgres.
Based on your input, the following should get you started using MySQL:
SELECT
map_id
FROM TableB
JOIN Table_A
ON TableB.table_a_id = Table_A.id
AND
((Table_A.data_x = 'person')
OR
(Table_A.data_y = '200')
)
GROUP BY map_id
HAVING COUNT(table_a_id) = 2
;
See it in action: SQL Fiddle.
Update
As Erwin Brandstetter made explicit: If the data can't be trusted to be inherently consistent (along the lines of your inquiry), one option is:
SELECT map_id FROM (
SELECT map_id, 'data_x' t
FROM TableB B JOIN Table_A A ON B.table_a_id = A.id AND A.data_x = 'person'
UNION
SELECT map_id, 'data_y'
FROM TableB B JOIN Table_A A ON B.table_a_id = A.id AND A.data_y = '200'
) T
GROUP BY map_id
HAVING COUNT(DISTINCT t) = 2
;
This should ensure "at least one each". (Alternatives have been suggested by others.) To get "exactly one each", you could try
SELECT map_id FROM (
SELECT map_id, 'data_x' t, data_y
FROM TableB B JOIN Table_A A ON B.table_a_id = A.id AND A.data_x = 'person'
UNION
SELECT map_id, 'data_y', data_y
FROM TableB B JOIN Table_A A ON B.table_a_id = A.id AND A.data_y = '200'
) T
GROUP BY map_id
HAVING COUNT(DISTINCT t) = 2 AND COUNT(DISTINCT data_y) = 2
;
See it in action (with additional test data): SQL Fiddle.
And it works in PostgreSQL as well: SQL Fiddle
Please comment if and as this requires adjustment / further detail.
Join the 2 tables, group by map_id, use conditional counting with either count() or sum(), and filter in having clause (I use mysql syntax below):
select map_id,
sum(
case
when a.data_x='person' or a.data_y='200' then 1
else 0
end
) as matches
from a
inner join b on a.id=b.a_id
group by b.map_id
having matches=2
The above query assumes that you cannot have more than one record for any map_id where data_x is person or data_y is 200. If this assumption is incorrect, then you need to use either exists subqueries or 2 derived tables.
Sounds like you want a standard INNER JOIN.
But I do beg to differ on your result:
map_id if it has an entry in table_a for both data_x = 'person' and data_y = '200'
There is not a record in your data set that has both 'person' and data_y = '200' and therefore no mp_id can be returned
Here is a typical INNER JOIN relating to your narrative.
SELECT DISTINCT
b.map_id
FROM
TableA a
INNER JOIN TableB b
ON a.id = b.table_a_id
WHERE
a.data_x = 'person'
AND a.data_y = '200'
If more than one map_id exists with data_x = 'person' and data_y = '200' then you will get multiple results but only 1 row per map_id
If you want the map_id(s) for records with data_x = 'person' or data_y = '200' then switch the and in the where statement to or and you will receive map_id 7 & 8.
SELECT DISTINCT
b.map_id
FROM
TableA a
INNER JOIN TableB b
ON a.id = b.table_a_id
WHERE
a.data_x = 'person'
OR a.data_y = '200'
Note this encompasses (7,1)(8,2) because 1 & 2 both have data_x = 'person' and then (7,3) because 3 has data_y = '200' therefore it would return map_id 7 & 8.
select map_id from
table_b b
left outer join table_a a1 on (b.table_a_id = a1.id and a1.data_x = 'person')
left outer join table_a a2 on (b.table_a_id = a2.id and a2.data_y = '200')
group by map_id
having count(a1.id) > 0 and count(a2.id) > 0
Lets do it simple:
SELECT * FROM
(
SELECT map_id
FROM table_a a1
inner join TableB b1 ON a1.id = b1.table_a_id
where a1.data_x = 'person'
) as p
inner join
(
SELECT map_id
FROM table_a a1
inner join TableB b1 ON a1.id = b1.table_a_id
where a1.data_y = '200'
) as q
on p.map_id = q.map_id
You may replace SELECT * FROM with SELECT p.map_id FROM.
You may add more sub-set-joins to have more conditions.
sql-fiddle

TSQL conditional join for values in second table

I want to do conditional join on two tables and wanted to join with highest status in the second table. The status values are Assigned, Booked, Delivery and Closed.
SELECT
CPC.CpcID, StatusFlow = CPC.Status, Orders.CarModel, EnquiryLog.EnquiryStatus
FROM
CPC
INNER JOIN
Orders ON CPC.CpcID = Orders.CpcID
INNER JOIN
EnquiryLog ON CPC.CpcID = EnquiryLog.CpcID
WHERE
CPC.CpcID = '24092015/12'
So in this case it should show only one row with EnquiryStatus 'Delivery' but based on my query the result is:
SQL query output:
If I got it the right way:
SELECT CPC.CpcID, StatusFlow = CPC.Status, Orders.CarModel, ca.EnquiryStatus
FROM CPC
INNER JOIN Orders ON CPC.CpcID = Orders.CpcID
CROSS APPLY(SELECT TOP 1 * FROM EnquiryLog WHERE CPC.CpcID = EnquiryLog.CpcID
ORDER BY CASE EnquiryStatus
WHEN 'CLOSED' THEN 1
WHEN 'DELIVERY' THEN 2
WHEN 'BOOKED' THEN 3
WHEN 'ASSIGNED' THEN 4 END) ca
WHERE CPC.CpcID='24092015/12'

Counting groups of 3? Possible?

I have the following query that is selecting groups of entries of 3 that match and don't exist in the second table.
I need to find a way to return via 'count_result' the count of the number of groups found, not individual entry ids as below.
How can I achieve this?
SELECT COUNT(sub.entry_id) as count_result
FROM exp_submissions AS sub
LEFT JOIN exp_judging_portfolios AS jud1 ON sub.entry_id = jud1.entry_id_1
LEFT JOIN exp_judging_portfolios AS jud2 ON sub.entry_id = jud1.entry_id_2
LEFT JOIN exp_judging_portfolios AS jud3 ON sub.entry_id = jud1.entry_id_3
WHERE jud1.entry_id_1 IS NULL
AND jud2.entry_id_2 IS NULL
AND jud3.entry_id_3 IS NULL
AND sub.member_group = 6
AND sub.type_id = 1
GROUP BY sub.member_id, sub.portfolio_number
HAVING count(sub.portfolio_number) = 3
No subquery necessary.
SELECT COUNT(DISTINCT(sub.member_id, sub.portfolio_number)) as count_result
FROM exp_submissions AS sub
LEFT JOIN exp_judging_portfolios AS jud1 ON sub.entry_id = jud1.entry_id_1
LEFT JOIN exp_judging_portfolios AS jud2 ON sub.entry_id = jud1.entry_id_2
LEFT JOIN exp_judging_portfolios AS jud3 ON sub.entry_id = jud1.entry_id_3
WHERE jud1.entry_id_1 IS NULL
AND jud2.entry_id_2 IS NULL
AND jud3.entry_id_3 IS NULL
AND sub.member_group = 6
AND sub.type_id = 1
GROUP BY sub.member_id, sub.portfolio_number
HAVING count(sub.portfolio_number) = 3
Use your query as the source for another query that just counts the results:
SELECT COUNT(*) FROM (
-- YOUR QUERY GOES HERE --
) AS t