Converting MySQL query that has a GROUP BY to MSSQL query - mysql

Hi I'm trying to convert my MySQL database to MSSQL one so what I'm doing is changing most of my queries. Then I encountered the GROUP BY problem in the MSSQL so I was stuck in this part.
tbl_assign role:
tar_id int IDENTITY(1,1) PRIMARY KEY,
tar_auth int NOT NULL,
tar_role varchar(255) NOT NULL,
tar_group int NOT NULL,
tar_division int NOT NULL,
tar_add varchar(255) NOT NULL,
tar_edit varchar(255) NOT NULL,
tar_view varchar(255) NOT NULL,
tar_delete varchar(255) NOT NULL,
tar_updated_date datetime NOT NULL,
tar_updated_by int NOT NULL,
tar_owner_id int NOT NULL,
tar_assign_date datetime NOT NULL,
tar_is_deleted tinyint NOT NULL
tbl_user:
u_id int NOT NULL,
u_uname varchar(255) NOT NULL,
u_pword varchar(255) NOT NULL,
u_fname varchar(255) NOT NULL,
u_mname varchar(255) NOT NULL,
u_lname varchar(255) NOT NULL,
u_spark_id varchar(255) NOT NULL,
u_email varchar(255) NOT NULL,
u_mobile decimal(11,0) NOT NULL,
u_address varchar(255) NOT NULL,
u_div int NOT NULL,
u_group int NOT NULL,
u_role int NOT NULL,
u_updated_by int NOT NULL,
u_updated_date datetime NOT NULL,
u_reg_date datetime NOT NULL,
is_active tinyint NOT NULL,
is_online tinyint NOT NULL,
u_photo varchar(MAX) NOT NULL
Sample data
tbl_user:
u_id - 20
u_uname - USERNAME
u_pword - *********
u_fname - Fname
u_mname - Mname
u_lname - Lname
u_spark_id - 1
u_email - email#sample.com
u_mobile - 0
u_address - Address
u_div - 0
u_group - 0
u_role - 0
u_updated_by - 1
u_updated_date - 2015-07-30 00:00:00
u_reg_date - 2015-04-08 00:00:00
is_active - 1
is_online - 1
u_photo - 0
tbl_assign_role:
tar_id - 264
tar_auth - 2
tar_role - 44
tar_group - 1
tar_division - 1
tar_add - 1-1-1-1-1-1-1-0-0-1-0-1-1-0-0
tar_edit - 1-1-1-1-1-1-1-0-0-1-0-1-1-0-0
tar_view - 1-1-1-1-1-1-1-0-0-1-0-1-1-0-0
tar_delete - 0-0-0-0-0-0-0-0-0-0-0-0-0-0-0
tar_updated_date - 2015-04-08 11:51:50.000
tar_updated_by - 17
tar_owner_id - 20
tar_assign_date - 2015-04-08 11:51:50.000
tar_is_deleted - 0
The tbl_assign_role has 9 more entries that has the same owner_id but different in the other columns.
MySQL query:
SELECT * FROM tbl_user
JOIN tbl_assign_role ON tbl_user.u_id = tbl_assign_role.tar_owner_id
WHERE is_active = 1
AND u_id !=1
AND tar_is_deleted = 0
GROUP BY tbl_assign_role.tar_owner_id
ORDER BY tbl_user.u_updated_date DESC
Result of MySQL query has only one row. It will give the first entry on the tbl_assign_role that has the same owner_id.
MSSQL query:
SELECT * FROM tbl_user
JOIN tbl_assign_role ON tbl_user.u_id = tbl_assign_role.tar_owner_id
WHERE is_active = 1
AND u_id !=1
AND tar_is_deleted = 0
ORDER BY tbl_user.u_updated_date DESC
Result of MSSQL query has ten rows. It will give all the result of the tbl_assign_role that has the same owner_id.
How can I achieve the same result in the MySQL query?

For many databases, and MS SQL Server is one, it is not possible to specify just a single column in the GROUP BY clause, but use the wildcard for all columns in the select clause. MySQL is the odd one here, it does allow this.
So, the message is simple. You MUST specify the non-aggregating columns in the GROUP BY clause. No exceptions.
You might want to consider use of ROW_NUMBER() OVER([partition by ...] order by ...) if you are really just wanting to return a single row per tar_owner_id.
SELECT
*
FROM (
SELECT
*
, ROW_NUMBER() OVER (PARTITION BY tbl_assign_role.tar_owner_id
ORDER BY tbl_user.u_updated_date DESC) AS rn
FROM tbl_user
JOIN tbl_assign_role
ON tbl_user.u_id = tbl_assign_role.tar_owner_id
WHERE is_active = 1
AND u_id != 1
AND tar_is_deleted = 0
) AS D
WHERE D.rn = 1
ORDER BY <<some column(s)>>
OFFSET 10 ROWS
FETCH NEXT 10 ROWS ONLY

Related

Getting the daywise report from two tables

I want to generate daywise report from two table
Table 1: opd
CREATE TABLE `opd` (
`id` int(50) NOT NULL,
`Date` date NOT NULL,
`time` time NOT NULL,
`opd_no` varchar(150) NOT NULL,
`patientid` varchar(150) DEFAULT NULL,
`name` varchar(150) NOT NULL,
);
Table structure
id
Date
Time
opd_no
Patient_id
name
1
2022-03-02
18:30:10
OPD/2122/1
PT01
Siba
2
2022-03-03
18:30:10
OPD/2122/2
PT02
Deba
3
2022-03-04
18:30:10
OPD/2122/3
PT03
Haris
4
2022-03-04
18:31:10
OPD/2122/4
PT04
ravish
Table 2: ipd_pn
CREATE TABLE `ipd_pn` (
`id` int(11) NOT NULL,
`ipd_no` varchar(40) DEFAULT NULL,
`patientid` varchar(170) NOT NULL,
`opd_reg_date` date DEFAULT NULL,
`time` time DEFAULT NULL,
`opd` varchar(40) DEFAULT NULL,
`date` date DEFAULT NULL,
)
Table structure
id
IPD_no
Patient_id
opd_Reg_date
time
opd_no
date
1
IPD/2122/1
PT01
2022-03-02
15:40:10
OPD/2122/1
2022-03-02
2
IPD/2122/2
PT03
2022-03-04
16:35:10
OPD/2122/3
2022-03-03
3
IPD/2122/3
PT02
2022-03-03
15:45:10
OPD/2122/2
2022-03-03
T tried to generate daywise how much opdno's generated by the below query
SELECT DATE(Date) AS date, COUNT(opdno) AS total_opd
FROM opd
WHERE (date BETWEEN '2022-02-1' AND '2022-03-28')
GROUP BY Date
Got output like below
date
total_opd
2022-03-02
1
2022-03-03
1
2022-03-04
2
But I can't generate how much ipd no's generated in this date period.
Please help.
I want a report like below
date
total_opd
total_ipd
2022-03-02
1
1
2022-03-03
1
1
2022-03-04
2
1
I tried with join query i.e
SELECT DATE(Date) AS date, COUNT(opdno) AS total_opd
FROM opd 0
INNER JOIN ipd_pn i ON o.Date = i.date
WHERE (date BETWEEN '2022-02-1' AND '2022-02-28')
GROUP BY Date
But that gives date ambiguity error.
So please try to help me how can I generate reports from both the tables.
You can try to use UNION ALL to combine two tables in a subquery then use a flag column to represent which row from opd or ipd_pn
SELECT date,
SUM(flag = 1) total_opd,
SUM(flag = 2) total_ipd
FROM (
SELECT DATE(Date) AS date, 1 flag
FROM opd
UNION ALL
SELECT Reg_date,2
FROM ipd_pn
) t1
WHERE date between '2022-02-1' and '2022-03-28'
GROUP BY Date
sqlfiddle

MYSQL LEFT JOIN Returning Null

I have three tables ,
Table 1 & table 2
Need to compare : component_name with product and component_version with version
if those values are same then fetch corresponding cvid from table2
Table 2 & table 3
Now with respect to the table 2 - cvid value is equal to table 3 - cvid value
(in-sence values should be equal) exact string match
i have tried with tables
Table 1 : upload_bom
CREATE TABLE IF NOT EXISTS `upload_bom` (
`component_name` varchar(100) NOT NULL,
`component_version` varchar(20) NOT NULL
)
Table 2 : cpe_cse
CREATE TABLE IF NOT EXISTS `cpe_cse` (
`id` varchar(20) NOT NULL,
`cpe` varchar(20) NOT NULL,
`argument` varchar(20) NOT NULL,
`vendor` varchar(100) NOT NULL,
`product` varchar(100) NOT NULL,
`version` varchar(20) NOT NULL,
`subversion` varchar(20) NOT NULL,
`platform` varchar(100) NOT NULL,
`cvid` varchar(20) NOT NULL
)
Table 3: cses
CREATE TABLE IF NOT EXISTS `cses` (
`cvid` varchar(20) NOT NULL,
`cvss` varchar(20) NOT NULL,
`description` varchar(5000) NOT NULL
)
Hence i have tried an code ,
but when i try to join cpe_csv.cvid = cses.cvid , i get null values but the same query works in sqlite not in MySQL
below is the query i tried
SELECT DISTINCT upload_bom.component_name, upload_bom.component_version, cpe_cse.cvid, cses.cvid, cses.description
FROM upload_bom
LEFT JOIN cpe_cse on upload_bom.component_name = cpe_cse.product AND upload_bom.component_version = cpe_cse.version
LEFT JOIN cses on cpe_cse.cvid = cses.cvid
i have to display something like
Select upload.bom_Component_name, upload_bom.component_version, cpe_cse.cvid, cses.cvid, cses.description
component_name , component_version , cpe_cse.cvid, cses.cvid, cses.description
"freebsd","1.1","cv1999-0001","cv1999-0001", "this is an software"
while joining two columns of two tables iam getting null values , i wann to over come it and join the tables

different values in same column, output in different columns

Currently I am working on a project, which has to do with Formula 1.
That's my structure of the table for results.
CREATE TABLE IF NOT EXISTS `races_results` (
`resultid` int(11) NOT NULL,
`seasonyear` int(4) NOT NULL,
`trackid` tinyint(2) NOT NULL,
`raceid` int(2) NOT NULL,
`session` tinyint(1) NOT NULL,
`q` int(11) NOT NULL,
`place` tinyint(2) NOT NULL,
`driverid` int(2) NOT NULL,
`teamid` int(2) NOT NULL,
`time` int(11) NOT NULL,
`laps` int(2) NOT NULL,
`status` varchar(3) NOT NULL
) ENGINE=MyISAM DEFAULT CHARSET=utf8;
My big problem is that I don't get the result in output as I want.
SELECT place, driverid, teamid, if(q=1, time, '') as time1, if(q=2, time, '') as time2, if(q=3, time, '') as time3
FROM `races_results`
WHERE `seasonyear` = 2015 AND `raceid` = 3 AND `session` = 2 AND `q` IN (1,2,3)
GROUP BY driverid
ORDER BY CASE WHEN q = 3 THEN place >= 1 AND place <= 10 END ASC, CASE WHEN q = 2 THEN place >= 11 AND place <= 16 END ASC, CASE WHEN q = 1 THEN place >= 17 AND place <= 22 END ASC
My target is that I want that the all times of a driver will show side by side and after this should be ordered by the participants of the sections.
After this I should have an output like this http://www.formula1.com/content/fom-website/en/championship/results/2015-race-results/2015-japan-results/qualifying.html
From your question I understand that the table races_results has a line for each result, so the times of the different qualifications are on different lines. To get these on one line you can do a join of the same table:
SELECT place, driverid, teamid, r1.time as time1, r2.time as time2, r3.time as time3
FROM races_results r1 LEFT JOIN races_results r2 on (r1.driverid=r2.driverid and r1.raceid=r2.raceid)
LEFT JOIN races_results r3 on (r1.driverid=r3.driverid and r1.raceid=r3.raceid)
WHERE r1.q=1 AND r2.q=2 AND r3.q=3 AND
`seasonyear` = 2015 AND `raceid` = 3 AND `session` = 2
GROUP BY driverid
ORDER BY place;
I assume:
that there is always a result for q=1;
the driverid and raceid are unique for a race on a specific year for a driver;
you want to order by place.

Performance of MySQL Query

I have inherited some code, the original author is not contactable and I would be extremely grateful for any assistance as my own MySQL knowledge is not great.
I have the following query that is taking around 4 seconds to execute, there is only around 20,000 rows of data in all the tables combined so I suspect the query could be made more efficient, perhaps by splitting it into more than one query, here it is:
SELECT SQL_CALC_FOUND_ROWS ci.id AS id, ci.customer AS customer, ci.installer AS installer, ci.install_date AS install_date, ci.registration AS registration, ci.wf_obj AS wf_obj, ci.link_serial AS link_serial, ci.sim_serial AS sim_serial, sc.call_status AS call_status
FROM ap_servicedesk.corporate_installs AS ci
LEFT JOIN service_calls AS sc ON ci.wf_obj = sc.wf_obj
WHERE ci.acc_id = 3
GROUP BY ci.id
ORDER BY link_serial
asc
LIMIT 40, 20
Can anyone spot any way to make this more efficient, thanks.
(Some values are set as variables but running the above query in PHPMyAdmin takes ~4secs)
The id column is the primary index.
More Info as requested:
corporate_installs table:
Field Type Null Key Default Extra
id int(11) NO PRI NULL auto_increment
customer varchar(800) NO NULL
acc_id varchar(11) NO NULL
installer varchar(50) NO NULL
install_date varchar(50) NO NULL
address_name varchar(30) NO NULL
address_street varchar(40) NO NULL
address_city varchar(30) NO NULL
address_region varchar(30) NO NULL
address_post_code varchar(10) NO NULL
latitude varchar(15) NO NULL
longitude varchar(15) NO NULL
registration varchar(50) NO NULL
driver_name varchar(50) NO NULL
vehicle_type varchar(50) NO NULL
make varchar(50) NO NULL
model varchar(50) NO NULL
vin varchar(50) NO NULL
wf_obj varchar(50) NO NULL
link_serial varchar(50) NO NULL
sim_serial varchar(50) NO NULL
tti_inv_no varchar(50) NO NULL
pro_serial varchar(50) NO NULL
eco_serial varchar(50) NO NULL
eco_bluetooth varchar(50) NO NULL
warranty_expiry varchar(50) NO NULL
project_no varchar(50) NO NULL
status varchar(15) NO NULL
service_calls table:
Field Type Null Key Default Extra
id int(11) NO PRI NULL auto_increment
acc_id int(15) NO NULL
ciid int(11) NO NULL
installer_job_no varchar(50) NO NULL
installer_inv_no varchar(50) NO NULL
engineer varchar(50) NO NULL
request_date varchar(50) NO NULL
completion_date varchar(50) NO NULL
call_status varchar(50) NO NULL
registration varchar(50) NO NULL
wf_obj varchar(50) NO NULL
driver_name varchar(50) NO NULL
driver_phone varchar(50) NO NULL
team_leader_name varchar(50) NO NULL
team_leader_phone varchar(50) NO NULL
servicing_address varchar(150) NO NULL
region varchar(50) NO NULL
post_code varchar(50) NO NULL
latitude varchar(50) NO NULL
longitude varchar(50) NO NULL
incident_no varchar(50) NO NULL
service_type varchar(20) NO NULL
fault_description varchar(50) NO NULL
requested_action varchar(50) NO NULL
requested_replacemt varchar(100) NO NULL
fault_detected varchar(50) NO NULL
action_taken varchar(50) NO NULL
parts_used varchar(50) NO NULL
new_link_serial varchar(50) NO NULL
new_sim_serial varchar(50) NO NULL
(Apologies for the formatting, I did the best I could)
Let me know if you need more info thanks.
Further info (I did the query again with EXPLAIN):
id select_type table type possible_keys key key_len ref rows Extra
1 SIMPLE ci ALL acc_id NULL NULL NULL 7227 Using where; Using temporary; Using filesort
1 SIMPLE sc ALL NULL NULL NULL NULL 410
Add indices on the two wf_obj columns, the link_serial column (you may also need an index on the acc_id, too).
Then try this version:
SELECT ...
FROM
( SELECT *
FROM ap_servicedesk.corporate_installs
WHERE acc_id = 3
ORDER BY link_serial ASC
LIMIT 60
) AS ci
LEFT JOIN service_calls AS sc
ON sc.PK = --- the PRIMARY KEY of the table
( SELECT PK
FROM service_calls AS scm
WHERE ci.wf_obj = scm.wf_obj
ORDER BY scm. --- whatever suits you
LIMIT 1
)
ORDER BY ci.link_serial ASC
LIMIT 20 OFFSET 40
The ORDER BY scm.SomeColumn is needed not for performance but to get consistent results. Your query as it is, is joining a row from the first table to all related rows of the second table. But the final GROUP BY aggregates all these rows (of the second table), so your SELECT ... sc.call_status picks a more or less random call_status from one of these rows.
The first place I'd look on this would have to be indexes.
There is a group on ci.id which is the PK which is fine, however you are ordering by link_ser (source table unspecified) and you are selecting based on ci.acc_id.
If you add an extra key on the table corp_installs for the field acc_id then that alone should help increase performance as it will be usable for the WHERE clause.
Looking further you have ci.wf_obj = sc.wf_obj within the join. Joining on a VARCHAR will be SLOW, and you are not actually using this as part of the selection criteria and so a SUBQUERY may be your friend, consider the following
SELECT
serviceCallData.*,
sc.call_status AS call_status
FROM (
SELECT
SQL_CALC_FOUND_ROWS AS found_rows,
ci.id AS id,
ci.customer AS customer,
ci.installer AS installer,
ci.install_date AS install_date,
ci.registration AS registration,
ci.wf_obj AS wf_obj,
ci.link_serial AS link_serial,
ci.sim_serial AS sim_serial
FROM ap_servicedesk.corporate_installs AS ci
WHERE ci.acc_id = 3
GROUP BY ci.id
ORDER BY ci.link_serial ASC
LIMIT 40, 20
) AS serviceCallData
LEFT JOIN serice_calls AS sc ON serviceCallData.wf_obj = sc.wf_obj
In addition to this, change that (acc_id) key to be (acc_id, link_serial) as then it will be usable in the sort. Also add a key on (wf_obj) into serice_calls.
This will select the 20 rows from the corpoprate_installs table and then only join them onto the service_calls table using the inefficient VARCHAR join
I hope this is of help
I think the SQL_CALC_FOUND_ROWS option used with a join and a group by could be degrading the performance (look here for some tests, info on SQL_CALC_FOUND_ROWS here). It seems in facts that indexes are not used in that case.
Try replacing your query with two separate queries, the one with the LIMIT followed by a COUNT().

Assistance with MySQL JOIN

I have two tables, one with categories and subcategories. Each category and subcategory has an id and if it's a subcategory, it's got a topid != 0 referring what it's a subcategory of. The other table "markers" has a field 'cat' which correlates with the category field 'name' Now I want to select everything from markers with category.id = 4 OR category.topid = 4 so I tried this query:
SELECT * FROM `xymply_markers`
JOIN `xymply_categories`
ON xymply_markers.cat = xymply_categories.name
WHERE xymply_categories.topid=4
OR xymply_categories.id=4
Which doesn't return me anything even tho I do have such elements in my table "markers". Any assistance would be appreciated!
Table schemas:
`xymply_categories` (
`id` int(11) NOT NULL auto_increment,
`topid` int(11) NOT NULL,
`name` text NOT NULL,
PRIMARY KEY (`id`)
) ENGINE=MyISAM AUTO_INCREMENT=15 DEFAULT CHARSET=utf8 AUTO_INCREMENT=15 ;
`xymply_markers` (
`created` date NOT NULL,
`id` int(11) NOT NULL auto_increment,
`sdate` date NOT NULL,
`hdate` date NOT NULL,
`name` varchar(60) NOT NULL,
`address` varchar(80) NOT NULL,
`unit` varchar(6) NOT NULL,
`lat` decimal(10,7) NOT NULL,
`lng` decimal(10,7) NOT NULL,
`type` varchar(30) NOT NULL,
`userid` int(11) NOT NULL,
`adtext` text NOT NULL,
`phone` varchar(20) NOT NULL,
`email` varchar(30) NOT NULL,
`url` varchar(30) NOT NULL,
`cat` varchar(4) NOT NULL,
UNIQUE KEY `id` (`id`)
) ENGINE=MyISAM AUTO_INCREMENT=151 DEFAULT CHARSET=utf8 AUTO_INCREMENT=151 ;
Sample Data:
xymply_categories:
id 1
topid 0
name 'vehicle'
--------------
id 2
topid 1
name 'bike'
--------------
id 3
topid 1
name 'truck'
xymply_markers:
id 1
sdate 2012-03-01
hdate 2012-04-01
name 'TEST'
address '1234 TEST'
unit''
lat 49.0
lng -123.0
adtext 'TEST'
phone '1234567890'
email 'email#email.com'
url 'www.url.com'
cat 'bike'
--------------
id 1
sdate 2012-03-01
hdate 2012-04-01
name 'TEST'
address '1234 TEST'
unit''
lat 49.5
lng -123.5
adtext 'TEST'
phone '1234567890'
email 'email#email.com'
url 'www.url.com'
cat 'vehicle'
One problem is that the xymply_markers.cat field is VARCHAR(4) but the xymply_categories.name field is TEXT, and contains values longer than 4 characters. Either you're not giving us the accurate schema, or you're confused about which columns join, or you're never going to see any trucks or vehicles. Columns which join should have the same type almost without exception (I've never seen a good reason for an exception).
You are then asking about id = 4 or topid = 4, but the sample data you show only has id = 1 or topid = 1. Do you actually have data where id = 4 or topid = 4 in the system?
Between these two lots of confusion, it is hard to know what we're up against. If you have data that joins and has the relevant topid or id values, then your query should work.
I have a field called 'id' in both tables. How can I control which one I'm accessing with PHP after I read data into the array with $row = #mysql_fetch_assoc($result)?
The simplest way is to ensure that each result column has a unique name, creating one with an 'alias', as in:
SELECT c.id AS category_id,
c.topid,
c.name AS category_name,
m.id AS marker_id,
m.name AS marker_name,
...
PHP will associate the alias names with the the data in the row.