difference between 2 columns (array of strings) and show where the difference is - sql-server-2008

I have created a query to capture Comments by teachers from two different databases. The teachers are adding comments to the front end and they are sent to two different databases. I am able to show the comments for the student for a subject from both the databases. But, I also have to find the minor differences between the two comments coming from both the databases.
I am so far able to compare the two columns (containing the teachers comments). and show null if the two columns are the same. But, I am not able to show where the difference between the two column values is.
Select distinct t1.SStudentID as SStudent, t2.AStudentId as AStudent , t1.Result as Result1, t2.EnteredValue as Result2,
COALESCE(NULLIF(t1.Result COLLATE Latin1_General_CI_AI, t2.EnteredValue COLLATE Latin1_General_CI_AI),NULLIF(t2.EnteredValue COLLATE Latin1_General_CI_AI, t1.Result COLLATE Latin1_General_CI_AI)) as NULL_IF_SAME
from
(SELECT distinct r.ID as SStudentID,r.ClassCode,r.TopicComment as Result
FROM [Database1].[dbo].[StudentAssessmentMarkBook] r
where r.classcode like '9%' and r.TopicComment != ''
) t1
join
(SELECT distinct a.Code as AStudentId ,c.code as ClassCode, ar.EnteredValue
FROM [Database2].[dbo].[AssessmentItem] a
join [Database2].[dbo].[ClassGroup] c on c.SubjectId=a.SubjectId
join [Database2].[dbo].[Result] ar on ar.AssessmentItemId = a.Id
where c.code like '9%' and a.code like '%COM%') t2 on (t1.ClassCode COLLATE Latin1_General_CI_AI = t2.code COLLATE Latin1_General_CI_AI )
where (t1.SStudentID = t2.AStudentId)
I now want to show the difference between Result1 and Result2 columns and display in new column where the difference is.

Related

MySQL Sum even if records doesnt exist [duplicate]

I need to retrieve all default settings from the settings table but also grab the character setting if exists for x character.
But this query is only retrieving those settings where character is = 1, not the default settings if the user havent setted anyone.
SELECT `settings`.*, `character_settings`.`value`
FROM (`settings`)
LEFT JOIN `character_settings`
ON `character_settings`.`setting_id` = `settings`.`id`
WHERE `character_settings`.`character_id` = '1'
So i should need something like this:
array(
'0' => array('somekey' => 'keyname', 'value' => 'thevalue'),
'1' => array('somekey2' => 'keyname2'),
'2' => array('somekey3' => 'keyname3')
)
Where key 1 and 2 are the default values when key 0 contains the default value with the character value.
The where clause is filtering away rows where the left join doesn't succeed. Move it to the join:
SELECT `settings`.*, `character_settings`.`value`
FROM `settings`
LEFT JOIN
`character_settings`
ON `character_settings`.`setting_id` = `settings`.`id`
AND `character_settings`.`character_id` = '1'
When making OUTER JOINs (ANSI-89 or ANSI-92), filtration location matters because criteria specified in the ON clause is applied before the JOIN is made. Criteria against an OUTER JOINed table provided in the WHERE clause is applied after the JOIN is made. This can produce very different result sets. In comparison, it doesn't matter for INNER JOINs if the criteria is provided in the ON or WHERE clauses -- the result will be the same.
SELECT s.*,
cs.`value`
FROM SETTINGS s
LEFT JOIN CHARACTER_SETTINGS cs ON cs.setting_id = s.id
AND cs.character_id = 1
If I understand your question correctly you want records from the settings database if they don't have a join accross to the character_settings table or if that joined record has character_id = 1.
You should therefore do
SELECT `settings`.*, `character_settings`.`value`
FROM (`settings`)
LEFT OUTER JOIN `character_settings`
ON `character_settings`.`setting_id` = `settings`.`id`
WHERE `character_settings`.`character_id` = '1' OR
`character_settings`.character_id is NULL
You might find it easier to understand by using a simple subquery
SELECT `settings`.*, (
SELECT `value` FROM `character_settings`
WHERE `character_settings`.`setting_id` = `settings`.`id`
AND `character_settings`.`character_id` = '1') AS cv_value
FROM `settings`
The subquery is allowed to return null, so you don't have to worry about JOIN/WHERE in the main query.
Sometimes, this works faster in MySQL, but compare it against the LEFT JOIN form to see what works best for you.
SELECT s.*, c.value
FROM settings s
LEFT JOIN character_settings c ON c.setting_id = s.id AND c.character_id = '1'
For this problem, as for many others involving non-trivial left joins such as left-joining on inner-joined tables, I find it convenient and somewhat more readable to split the query with a with clause. In your example,
with settings_for_char as (
select setting_id, value from character_settings where character_id = 1
)
select
settings.*,
settings_for_char.value
from
settings
left join settings_for_char on settings_for_char.setting_id = settings.id;
The way I finally understand the top answer is realising (following the Order Of Execution of the SQL query ) that the WHERE clause is applied to the joined table thereby filtering out rows that do not satisfy the WHERE condition from the joined (or output) table. However, moving the WHERE condition to the ON clause applies it to the individual tables prior to joining. This enables the left join to retain rows from the left table even though some column entries of those rows (entries from the right tables) do not satisfy the WHERE condition.
The result is correct based on the SQL statement. Left join returns all values from the right table, and only matching values from the left table.
ID and NAME columns are from the right side table, so are returned.
Score is from the left table, and 30 is returned, as this value relates to Name "Flow". The other Names are NULL as they do not relate to Name "Flow".
The below would return the result you were expecting:
SELECT a.*, b.Score
FROM #Table1 a
LEFT JOIN #Table2 b
ON a.ID = b.T1_ID
WHERE 1=1
AND a.Name = 'Flow'
The SQL applies a filter on the right hand table.

Order by count(*) of my second table takes long time

Let's assume I have 2 tables. One contains car manufacturer's names and their IDs, the second contains information about car models. I need to select few of them from the first table, but order them by quantity of linked from the second table data.
Currently, my query looks like this:
SELECT DISTINCT `manufacturers`.`name`,
`manufacturers`.`cars_link`,
`manufacturers`.`slug`
FROM `manufacturers`
JOIN `cars`
ON manufacturers.cars_link = cars.manufacturer
WHERE ( NOT ( `manufacturers`.`cars_link` IS NULL ) )
AND ( `cars`.`class` = 'sedan' )
ORDER BY (SELECT Count(*)
FROM `cars`
WHERE `manufacturers`.cars_link = `cars`.manufacturer) DESC
It was working ok for my table of scooters which size is few dozens of mb. But now i need to do the same thing for the cars table, which size is few hundreds megabytes. The problem is that the query takes very long time, sometimes it even causes nginx timeout. Also, i think, that i have all the necesary database indexes. Is there any alternative for the query above?
lets try to use subquery for your count instead.
select * from (
select distinct m.name, m.cars_link, m.slug
from manufacturers m
join cars c on m.cars_link=c.manufacturer
left join
(select count(1) ct, c1.manufacturer from manufacturers m1
inner join cars_link c2 on m1.cars_link=c2.manufacturer
where coalesce(m1.cars_link, '') != '' and c1.class='sedan'
group by c1.manufacturer) as t1
on t1.manufacturer = c.manufacturer
where coalesce(m.cars_link, '') != '' and c.class='sedan') t2
order by t1.ct

MINUS operator in MySQL query [duplicate]

I am trying to perform a MINUS operation in MySql.I have three tables:
one with service details
one table with states that a service is offered in
another table (based on zipcode and state) shows where this service is not offered.
I am able to get the output for those two select queries separately. But I need a combined statement that gives the output as
'SELECT query_1 - SELECT query_2'.
Service_Details Table
Service_Code(PK) Service Name
Servicing_States Table
Service_Code(FK) State Country PK(Service_Code,State,Country)
Exception Table
Service_Code(FK) Zipcode State PK(Service_Code,Zipcode,State)
MySql does not recognise MINUS and INTERSECT, these are Oracle based operations. In MySql a user can use NOT IN as MINUS (other solutions are also there, but I liked it lot).
Example:
select a.id
from table1 as a
where <condition>
AND a.id NOT IN (select b.id
from table2 as b
where <condition>);
MySQL Does not supports MINUS or EXCEPT,You can use NOT EXISTS, NULL or NOT IN.
Here's my two cents... a complex query just made it work, originally expressed with Minus and translated for MySql
With MINUS:
select distinct oi.`productOfferingId`,f.name
from t_m_prod_action_oitem_fld f
join t_m_prod_action_oitem oi
on f.fld2prod_action_oitem = oi.oid;
minus
select
distinct r.name,f.name
from t_m_prod_action_oitem_fld f
join t_m_prod_action_oitem oi
on f.fld2prod_action_oitem = oi.oid
join t_m_rfs r
on r.name = oi.productOfferingId
join t_m_attr a
on a.attr2rfs = r.oid and f.name = a.name;
With NOT EXISTS
select distinct oi.`productOfferingId`,f.name
from t_m_prod_action_oitem_fld f
join t_m_prod_action_oitem oi
on f.fld2prod_action_oitem = oi.oid
where not exists (
select
r.name,f.name
from t_m_rfs r
join t_m_attr a
on a.attr2rfs = r.oid
where r.name = oi.productOfferingId and f.name = a.name
The tables have to have the same columns, but I think you can achieve what you are looking for with EXCEPT... except that EXCEPT only works in standard SQL! Here's how to do it in MySQL:
SELECT * FROM Servicing_states ss WHERE NOT EXISTS
( SELECT * FROM Exception e WHERE ss.Service_Code = e.Service_Code);
http://explainextended.com/2009/09/18/not-in-vs-not-exists-vs-left-join-is-null-mysql/
Standard SQL
SELECT * FROM Servicing_States
EXCEPT
SELECT * FROM Exception;
An anti-join pattern is the approach I typically use. That's an outer join, to return all rows from query_1, along with matching rows from query_2, and then filtering out all the rows that had a match... leaving only rows from query_1 that didn't have a match. For example:
SELECT q1.*
FROM ( query_1 ) q1
LEFT
JOIN ( query_2 ) q2
ON q2.id = q1.id
WHERE q2.id IS NULL
To emulate the MINUS set operator, we'd need the join predicate to compare all columns returned by q1 and q2, also matching NULL values.
ON q1.col1 <=> q2.col2
AND q1.col2 <=> q2.col2
AND q1.col3 <=> q2.col3
AND ...
Also, To fully emulate the MINUS operation, we'd also need to remove duplicate rows returned by q1. Adding the DISTINCT keyword would be sufficient to do that.
In case the tables are huge and are similar, one option is to save the PK to new tables. Then compare based only on the PK. In case you know that the first half is identical or so add a where clause to check only after a specific value or date .
create table _temp_old ( id int NOT NULL PRIMARY KEY )
create table _temp_new ( id int NOT NULL PRIMARY KEY )
### will take some time
insert into _temp_old ( id )
select id from _real_table_old
### will take some time
insert into _temp_new ( id )
select id from _real_table_new
### this version should be much faster
select id from _temp_old to where not exists ( select id from _temp_new tn where to.id = tn.id)
### this should be much slower
select id from _real_table_old rto where not exists ( select id from _real_table_new rtn where rto.id = rtn.id )

mysql search in multiple joined tables (7 tables with 6 joins)

I'm trying to search in multiple tables. I followed normalization rules in designing my database. I have 7 tables explained in this schema.
I built a view to get the interesting columns out of these tables.
ALTER ALGORITHM = UNDEFINED DEFINER = `root`#`localhost` SQL SECURITY DEFINER VIEW `port_data_view` AS
SELECT `s`.`tel_subscriber_number` AS `phone`
,`s`.`tel_subscriber_name` AS `tel_subscriber_name`
,`thp`.`tel_subscriber_has_port_startdate` AS `Reservation_Date`
,`p`.`port_serialnum` AS `port_number`
,`p`.`port_current_speed` AS `port_speed`
,`p`.`idport` AS `idport`
,`p`.`port_status` AS `port_status`
,`isp`.`isp_en_name` AS `isp_en_name`
,`c`.`contract_enname` AS `contract_enname`
,`mdf`.`mdf_room_arname` AS `mdf_room_arname`
,`mdf`.`idmdf_room` AS `idmdf_room`
,`c`.`idcontract` AS `contract_id`
,`isp`.`idisp` AS `isp_id`
FROM (
(
(
(
(
(
`tel_subscriber` `s` INNER JOIN `tel_subscriber_has_port` `thp` ON (
(
(`s`.`idtel_subscriber` = `thp`.`tel_subscriber_idtel_subscriber`)
AND (`thp`.`tel_subscriber_has_port_status` = 'active')
)
)
) INNER JOIN `port` `p` ON ((`p`.`idport` = `thp`.`port_idport`))
) INNER JOIN `isp_has_port` `ihp` ON (
(
(`p`.`idport` = `ihp`.`port_idport`)
AND (`ihp`.`isp_has_port_status` = 'Active')
)
)
) INNER JOIN `isp` ON ((`ihp`.`isp_idisp` = `isp`.`idisp`))
) INNER JOIN `mdf_room` `mdf` ON ((`mdf`.`idmdf_room` = `p`.`mdf_room_idmdf_room`))
) INNER JOIN `contract` `c` ON ((`c`.`idcontract` = `p`.`contract_idcontract`))
)
as you can see there is 7 joins between these tables.
when I try to search in this view about any factor (1 or multiple criteria), it's too slow (more than 10 minutes with no result).
when I try to search in each table I get the result with maximum time of 5 seconds.
The count of records in each table are:
mdf_room: 538
tel_subscriber: 798019
tel_subscriber_has_port: 790989
port: 797174
isp: 22
isp_has_port: 810676
contract: 25
I tried to explain the query in here.
what can I do to search with a proper time about the information I want?
Try to use Join with Subquery First.
The problem is that you are joining 7 tables (4 of them have about one million records) which will get mysql server crazy.
To overcome this problem try to join with the interesting data you want, by joining with subquery including the condition inside of it.
Example:
First case:
SELECT t1.*, t2.*
from table1 t1 join table2 t2 on (t1.id = t2.fkid)
where t2.attr1 = 'foo'
Second case:
select t1.*, t2_1.*
from table1 t1 join (select t2.* from table2 t2 where t2.attr1 = 'foo') t2_1
on t1.id = t2_1.fkid
when you have so many joins this would be useful. with no more than 2 or 3 joins the traditional join then search way would be useful too.
Use EXPLAIN on your query to understand MySQL's query plan. Create appropriate indices to prevent table scans. Here are some links for your reference:
Understanding the Query Execution Plan
MySQL Explain Explained
If you post the results of the EXPLAIN query, you may get more specific suggestions.

Mysql Join After SELECT

I need to select 50 items that satisfy search criteria from the item list.
I also need current quantity of them on sales.
Item list has more than 100 000 records
This is what i do now:
SELECT
items.id,
CONCAT(prod_codes.code,items.item) AS item,
items.price,
sold.count
FROM items
LEFT JOIN prod_codes ON items.prod_code_id=prod_codes.id
LEFT JOIN (
SELECT COUNT(*) AS count,CONCAT(prod_code,part_num) AS part
FROM `sold_items`
WHERE 1
GROUP BY CONCAT(prod_code,part_num)
ORDER BY count DESC
) AS sold ON sold.part=CONCAT(prod_codes.code,items.item)
WHERE active_status=1
AND CONCAT(items.prod_code,items.item) collate utf8_general_ci LIKE '%".$search_part."%'
AND items.description collate utf8_general_ci LIKE '%".$search_desc."%'
ORDER BY items.item
LIMIT 50
But this query takes over 20 seconds to execute. I'm sure there is better way to do it.
Replace this part of your WHERE clause
CONCAT(items.prod_code,items.item) collate utf8_general_ci LIKE '%".$search_part."%'
with this:
(items.prod_code collate utf8_general_ci LIKE '%".$search_part."%' OR items.item collate utf8_general_ci LIKE '%".$search_part."%')
That CONCAT() call is almost certainly bogging it down, because it can't use an index.
Also, you have SQL injection problems, etc. -- use prepared statements.
Finally, see #ypercube's comment; it's also a good point and will have a big impact.
Instead of nesting a subquery, you may try this:
SELECT
items.id,
CONCAT(prod_codes.code,items.item) AS item,
items.price,
COUNT(*) AS count
FROM items
LEFT JOIN prod_codes ON items.prod_code_id = prod_codes.id
LEFT JOIN sold_items sold ON sold.prod_code = prod_codes.code AND sold.part_num = items.item
WHERE active_status = 1
AND CONCAT(items.prod_code,items.item) collate utf8_general_ci LIKE '%".$search_part."%'
AND items.description collate utf8_general_ci LIKE '%".$search_desc."%'
ORDER BY items.item
GROUP BY prod_codes.code, items.item, items.id, items.price
LIMIT 50