SQL: only return results, after a join, where columns with different words represent the same idea (col 1 'dog' col 2 'hound' = match) - sql

SQL flavor is Mode's unique variation, that said SQL flavor doesn't really matter. If you can show me how to do it in one variety of SQL, I'll at least know what to Google to figure it out in this variation.
I'm joining two tables and trying to identify columns where the status of an item is the same, however the statuses are written differently between the two tables.
Table 1 columns:
Name
Number
Status (available, unavailable, inactive)
Table 2 columns:
Number
Status (unassigned, unavailable, retired)
Available = unassigned, unavailable = unavailable, inactive = retired.
I am trying to first compare available/unassigned line up, inactive/retired line up, etc. Then I'm trying to return only the results where both status columns do not match, but since they use different words for the same idea I just don't know how to do it.

I'd simply recode the Status values to numeric code in a WHERE clause like so
SELECT t1.*,t2.Status FROM t1
LEFT JOIN t2 ON t1.Number = t2.Number
WHERE CASE WHEN t1.Status = 'available' THEN 1
WHEN t1.Status = 'unavailable' THEN 2
WHEN t1.Status = 'inactive' THEN 3
END != CASE WHEN t2.Status = 'unassigned' THEN 1
WHEN t2.Status = 'unavailable' THEN 2
WHEN t2.Status = 'retired' THEN 3
END
http://sqlfiddle.com/#!9/39f243/2

You may use something like the below:
; With StatusMapping AS
(
SElect 'Available' T1Status , 'Unassigned' T2Status
UNION
SELECT 'Unavailable', 'Unavailable'
UNION
SELECT 'Inactive', 'Retired'
)
SELECT *
FROM T1
INNER JOIN StatusMapping M ON M.T1Status = T1.Status
INNER JOIN T2
ON T1.Number = T2.Number
AND T2.Status = M.T2Status

Related

Sql query to group and filter results

I need help with a logic in sql
I have two columns
Ticket no. Status
T1 CC
T1 CP
T1 CR
T1 CO
T2 CP
T2 CR
T2 CO
I want to exclude the entire group of Ticket no. that has the status CC. So in this case, after I run the query I should only get 3 records (i.e. T2 records coz T2 doesn’t have a ticket status CC.
Can anyone help me with a simple sql query please.
One method is not exists:
select t.*
from t
where not exists (select 1
from t t2
where t2.ticket_no = t.ticket_no and t2.status = 'CC'
);
In Teradata, though, the qualify clause might be simpler:
select t.*
from t
qualify sum(case when status = 'CC' then 1 else 0 end) over (partition by ticket_no) = 0;

SQL query to pull certain data from multiple tables based on values in a third

I have three tables that look something like this:
Table 1:
ID Number
Code
Amount
Indicator (I/A)
Table 2:
ID Number
Name
Address
Table 3:
Code
AName
AAddress
I need help writing a SQL query that will pull the Name & Address from Table 2 only if the Indicator in Table 1 is an "I", or instead will pull the AName & AAddress from Table 3 only if the Indicator in Table 1 is an "A".
I've been driving myself crazy over this! I've tried various IF and CASE statements, but have not gotten any closer to a solution.
I believe this will do the trick:
Select
Case T1.Indicator
When 'I' Then T2.Name
When 'A' Then T3.AName
End as "Name"
,Case T1.Indicator
When 'I' Then T2.Address
When 'A' Then T3.AAddress
End as "Address"
From Table1 as T1
Join Table2 as T2
on T1.IDNumber = T2.IDNumber
Join Table3 as T3
on T1.Code = T3.Code
Could this be what you are looking for? With coalesce, the first hit that is not null gets returned. So if we join table2 with the condition one.indicator = 'i' it will be null otherwise, instead giving the value from table3.
SELECT one.*, COALESCE(i.Name, a.AName) name, COALESCE(i.Address,a.AAddress) address
FROM table1 one
LEFT OUTER JOIN table2 i ON one.id = i.id AND one.indicator = 'i'
LEFT OUTER JOIN table3 a ON one.code = a.code AND one.indicator = 'a'
I had to assume that we wanted to join on code in the case of table3.
EDIT: This should at least work for sql server and mysql, if you're looking to solve it for another db type, this could be a suggestion for an approach that could work.

Optimizing tricky SQL search query

I am trying to come up with a simple, performant query for the following problem:
Let's say there are several entities (items) which all have a unique ID. The entities have a variable set of attributes (properties), which therefore have been moved to a separate table:
T_Items_Props
=======================
Item_ID Prop_ID Value
-----------------------
101 1 'abc'
101 2 '123'
102 1 'xyz'
102 2 '123'
102 3 '102'
... ... ...
Now I want to search for an item, that matches some specified search-criteria, like this:
<<Pseudo-SQL>>
SELECT Item_Id(s)
FROM T_Items_Props
WHERE Prop 1 = 'abc'
AND Prop 2 = '123'
...
AND Prop n = ...
This would be fairly easy if I had a table like Items(Id, Prop_1, Prop_2, ..., Prop_n). Then I could do a simple SELECT where the search criteria could simply (even programmatically) be inserted in the WHERE-clause, but in this case I would have to do something like:
SELECT t1.Item_ID
FROM T_Items_Props t1
, T_Items_Props t2
, ...
, T_Items_Props tn -- (depending on how many properties to compare)
AND t1.Item_ID = t2.Item_ID
AND t1.Prop_ID = 1 AND t1.Value = 'abc'
AND t2.Prop_ID = 2 AND t2.Value = '123'
...
AND tn.Prop_ID = n AND tn.Value = ...
Is there a better/simpler/faster way to do this?
To make the query more readable, you could do something like:
SELECT
t1.Item_ID
FROM
T_Items_Props t1
where convert(varchar(10), t1.Item_ID) + ';' + t1.Value in (
'1;abc',
'2;123',
...
)
NOTE: This assumes, that your IDs will not have more than 10 digets. It might also slow your query down, due to the extra type conversion and string concatanation.
You could count the number of correct Props. This isn't very good in case there could be duplicates. E.g.:
Prop_ID = 1 AND Value = 'abc'
Prop_ID = 2 AND Value = '123'
and the table would look like:
T_Items_Props
=======================
Item_ID Prop_ID Value
-----------------------
101 1 'abc'
101 1 'abc'
this would then be true, although it shouldn't.
But if you wanna give it a try, here's how:
SELECT nested.* FROM (
SELECT item_id, count(*) AS c FROM t_items_props
WHERE ((prop = 1 AND value = 'abc')
OR (prop = 2 AND value = '123')
... more rules here ...)
GROUP BY item_id) nested
WHERE nested.c > 2 ... number of rules ...
I've offered this in a previous post of similar querying intentions. The user could have 2 criteria one time, and five criteria another and wanted an easy way to build the SQL command. To simplify the need of having to add FROM tables and update the WHERE clause, you can simplify by doing joins and put that criteria right at the join level... So, each criteria is it's own set added to the mix.
SELECT
t1.Item_ID
FROM
T_Items_Props t1
JOIN T_Items_Props t2
on t1.Item_ID = t2.Item_ID
AND t2.Prop_ID = 2
AND t2.Value = '123'
JOIN T_Items_Props t3
on t1.Item_ID = t3.Item_ID
AND t3.Prop_ID = 6
AND t3.Value = 'anything'
JOIN T_Items_Props t4
on t1.Item_ID = t4.Item_ID
AND t4.Prop_ID = 15
AND t4.Value = 'another value'
WHERE
t1.Prop_ID = 1
AND t1.Value = 'abc'
Notice the primary query will always start with a minimum of the "T1" property/value criteria, but then, notice the JOIN clauses... they are virtually the same so it is very easy to implement via a loop... Just keep aliasing the T2, T3, T4... as needed. This will start with any items that meet the T1 criteria, but then also require all the rest to be found too.
You can use a join statement together with filtering or faceted search. It gives better performance because you can limit the search space. Here is a good example: Faceted Search (solr) vs Good old filtering via PHP?.

MYSQL join, return first matching row only from where join condition using OR

I'm having a problem with a particular MySQL query.
I have table1, and table2, table2 is joined onto table1.
Now the problem is that I am joining table2 to table1 with a condition that looks like:
SELECT
table1.*, table2.*
JOIN table2 ON ( table2.table1_id = table1.id
AND ( table2.lang = 'fr'
OR table2.lang = 'eu'
OR table2.lang = 'default') )
I need it to return only 1 row from table2, even though there might exists many table2 rows for the correct table1.id, with many different locales.
I am looking for a way to join only ONE row with a priority of the locales, first check for one where lang = something, then if that doesn't manage to join/return anything, then where lang = somethingelse, and lastly lang = default.
FR, EU can be different for many users, and rows in the database might exist for many different locales.. I need to select the most suitable ones with the correct fallback priority.
I tried doing the query above with a GROUP BY table2.table1_id, and it seemed to work, but I realised that if the best matching (first OR) was entered later in the table (higher primary ID) it would return 2nd or default priority as the grouped by row..
Any tips?
Thank you!
it still doesn't seem to "know" what t1.id is :(
Here follows my entire query, it goes to show table1 = _product_sku, table2 = _product_sku_data, t1 = ps, t2 = psd
SELECT ps.id, psd.description, psd.lang
FROM _product_sku ps
CROSS JOIN
( SELECT lang, title, description
FROM _product_sku_data
WHERE product_sku_id = ps.id
ORDER BY CASE WHEN lang='$this->profile_language_preference' THEN 0
WHEN lang='$this->browser_language' THEN 1
WHEN lang='default' THEN 2
ELSE 3
END
LIMIT 1
) AS psd
Edit:
This version uses variables to provide some sort of ranking within the available languages. Tables and test-data are not from the original question, but from the query OP provided as an answer.
It produced the expected results when I tried it:
SELECT id, description, lang
FROM
(
SELECT ps.id, psd.description, psd.lang,
CASE
WHEN #id != ps.id THEN #rownum := 1
ELSE #rownum := #rownum + 1
END AS rank,
#id := ps.id
FROM _product_sku ps
JOIN _product_sku_data psd ON ( psd.product_sku_id = ps.id )
JOIN ( SELECT #id:=NULL, #rownum:=0 ) x
ORDER BY id,
CASE WHEN lang='$this->profile_language_preference' THEN 0
WHEN lang='$this->browser_language' THEN 1
WHEN lang='default' THEN 2
ELSE 3
END
) x
WHERE rank = 1;
Old version which did not work, since ps.id is not known in the WHERE clause:
This one should return you the rows of table1 with the "best matching" row of table2 by using LIMIT 1 and ordering languages as defined:
SELECT t1.id, t2.lang, t2.some_column
FROM table1 t1
CROSS JOIN
( SELECT lang, some_column
FROM table2
WHERE table1_id = t1.id
ORDER BY CASE WHEN lang='fr' THEN 0
WHEN lang='eu' THEN 1
WHEN lang='default' THEN 2
ELSE 3
END
LIMIT 1
) t2

grouping records in one temp table

I have a table where one column has duplicate records but other columns are distinct. so something like this
Code SubCode version status
1234 D1 1 A
1234 D1 0 P
1234 DA 1 A
1234 DB 1 P
5678 BB 1 A
5678 BB 0 P
5678 BP 1 A
5678 BJ 1 A
0987 HH 1 A
So in the above table. subcode and Version are unique values whereas Code is repeated. I want to transfer records from the above table into a temporary table. Only records I would like to transfer are where ALL the subcodes for a code have status of 'A' and I want them in the temp table only once.
So from example above. the temporary table should only have
5678 and 0987 since all the subcodes relative to 5678 have status of 'A' and all subcodes for 0987 (it only has one) have status of A. 1234 is ommited because its subcode 'DB' has status of 'P'
I'd appreciate any help!
Here's my solution
SELECT Code
FROM
(
SELECT
Code,
COUNT(SubCode) as SubCodeCount
SUM(CASE WHEN ACount > 0 THEN 1 ELSE 0 END)
as SubCodeCountWithA
FROM
(
SELECT
Code,
SubCode,
SUM(CASE WHEN Status = 'A' THEN 1 ELSE 0 END)
as ACount
FROM CodeTable
GROUP BY Code, SubCode
) sub
GROUP BY Code
) sub2
WHERE SubCodeCountWithA = SubCodeCount
Let's break it down from the inside out.
SELECT
Code,
SubCode,
SUM(CASE WHEN Status = 'A' THEN 1 ELSE 0 END)
as ACount
FROM CodeTable
GROUP BY Code, SubCode
Group up the codes and subcodes (Each row is a distinct pairing of Code and Subcode). See how many A's occured in each pairing.
SELECT
Code,
COUNT(SubCode) as SubCodeCount
SUM(CASE WHEN ACount > 0 THEN 1 ELSE 0 END)
as SubCodeCountWithA
FROM
--previous
GROUP BY Code
Regroup those pairings by Code (now each row is a Code) and count how many subcodes there are, and how many subcodes had an A.
SELECT Code
FROM
--previous
WHERE SubCodeCountWithA = SubCodeCount
Emit those codes with have the same number of subcodes as subcodes with A's.
It's a little unclear as to whether or not the version column comes into play. For example, do you only want to consider rows with the largest version or if ANY subcde has an "A" should it count. Take 5678, BB for example, where version 1 has an "A" and version 0 has a "B". Is 5678 included because at least one of subcode BB has an "A" or is it because version 1 has an "A".
The following code assumes that you want all codes where every subcode has at least one "A" regardless of the version.
SELECT
T1.code,
T1.subcode,
T1.version,
T1.status
FROM
MyTable T1
WHERE
(
SELECT COUNT(DISTINCT subcode)
FROM MyTable T2
WHERE T2.code = T1.code
) =
(
SELECT COUNT(DISTINCT subcode)
FROM MyTable T3
WHERE T3.code = T1.code AND T3.status = 'A'
)
Performance may be abysmal if your table is large. I'll try to come up with a query that is likely to have better performance since this was off the top of my head.
Also, if you explain the full extent of your problem maybe we can find a way to get rid of that temp table... ;)
Here are two more possible methods. Still a lot of subqueries, but they look like they will perform better than the method above. They are both very similar, although the second one here had a better query plan in my DB. Of course, with limited data and no indexing that's not a great test. You should try all of the methods out and see which is best for your database.
SELECT
T1.code,
T1.subcode,
T1.version,
T1.status
FROM
MyTable T1
WHERE
EXISTS
(
SELECT *
FROM MyTable T2
WHERE T2.code = T1.code
AND T2.status = 'A'
) AND
NOT EXISTS
(
SELECT *
FROM MyTable T3
LEFT OUTER JOIN MyTable T4 ON
T4.code = T3.code AND
T4.subcode = T3.subcode AND
T4.status = 'A'
WHERE T3.code = T1.code
AND T3.status <> 'A'
AND T4.code IS NULL
)
SELECT
T1.code,
T1.subcode,
T1.version,
T1.status
FROM
MyTable T1
WHERE
EXISTS
(
SELECT *
FROM MyTable T2
WHERE T2.code = T1.code
AND T2.status = 'A'
) AND
NOT EXISTS
(
SELECT *
FROM MyTable T3
WHERE T3.code = T1.code
AND T3.status <> 'A'
AND NOT EXISTS
(
SELECT *
FROM MyTable T4
WHERE T4.code = T3.code
AND T4.subcode = T3.subcode
AND T4.status = 'A'
)
)
In your select, add a where clause that reads:
Select [stuff]
From Table T
Where Exists
(Select * From Table
Where Code = T.Code
And Status = 'A')
And Not Exists
(Select * From Table I
Where Code = T.Code
And Not Exists
(Select * From Table
Where Code = I.Code
And SubCode = I.SubCode
And Status = 'A'))
In English,
Show me the rows,
where there is at least one row with status 'A',
and there are NO rows with any specific subcode,
that do not have at least one row with that code/subcode, with status 'A'
INSERT theTempTable (Code)
SELECT t.Code
FROM theTable t
LEFT OUTER JOIN theTable subT ON (t.Code = subT.Code AND subT.status <> 'A')
WHERE subT.Code IS NULL
GROUP BY t.Code
This should do the trick. The logic is a little tricky, but I'll do my best to explain how it is derived.
The outer join combined with the IS NULL check allows you to search for the absence of a criteria. Combine that with the inverse of what you're normally looking for (in this case status = 'A') and the query succeeds when there are no rows that do not match. This is the same as ((there are no rows) OR (all rows match)). Since we know that there are rows due to the other query on the table, all rows must match.