How to Display Join table from another table? - mysql

I have problem with querying data in MySQL, I want my data to display like this:
id
Name
Kp
price
KI014
TPD
SS10
1000
SS11
2000
SS12
3000
KI015
ASD
SK14
1500
SK15
2500
My query is like this:
SELECT SQL_CALC_FOUND_ROWS produkreseller.IDRESELLER as ID,
masterreseller.NAMARESELLER as Name,
produk.KodeProduk as KP,
produkreseller.hargajual as Price
FROM produkreseller
INNER JOIN produk ON produkreseller.IdProduk = produk.IDPRODUK
INNER JOIN masterreseller ON produkreseller.IDRESELLER = masterreseller.idreseller
ORDER BY masterreseller.idreseller;
And the result from that query is:
id
Name
Kp
price
KI014
TPD
SS10
1000
KI014
TPD
SS11
2000
KI014
TPD
SS12
3000
KI015
ASD
SK14
1500
KI015
ASD
SK15
2500

The basic idea is to partition the data by IDRESELLER, check if the current record is the first record in that partition, and if so, include the value of IDRESELLER (or NAMARESELLER) in the result, otherwise include the empty string.
SELECT CASE WHEN ROW_NUMBER() OVER (PARTITION BY produkreseller.IDRESELLER) = 1
THEN produkreseller.IDRESELLER
ELSE '' END as ID,
CASE WHEN ROW_NUMBER() OVER (PARTITION BY produkreseller.IDRESELLER) = 1
THEN masterreseller.NAMARESELLER
ELSE '' END as Name,
produk.KodeProduk as KP,
produkreseller.hargajual as Price
FROM produkreseller
INNER JOIN produk ON produkreseller.IdProduk = produk.IDPRODUK
INNER JOIN masterreseller ON produkreseller.IDRESELLER = masterreseller.idreseller
ORDER BY id;
This solution makes use of window function ROW_NUMBER(). More information about window functions can be found in the documentation.
Update
In MySQL 5 there are no window functions but one can make use of variables:
SELECT CASE WHEN ID <> #last_id THEN ID ELSE '' END as ID,
CASE WHEN ID <> #last_id THEN Name ELSE '' END as Name,
KP, Price,
#last_id := ID
FROM (SELECT produkreseller.IDRESELLER as ID,
masterreseller.NAMARESELLER as Name,
produk.KodeProduk as KP,
produkreseller.hargajul as Price
FROM produkreseller
INNER JOIN produk ON produkreseller.IdProduk = produk.IDPRODUK
INNER JOIN masterreseller ON produkreseller.IDRESELLER = masterreseller.idreseller
ORDER BY id) products, (SELECT #last_id := '') x;
As I don't have a possibility to run those queries, consider them as untested sketches (that is they may contain errors).

Related

Multiple Filter inside SELECT SQL Statement

I'm writing a SQL Statement to get some values in a Recordset, which I'll use to transfer the result into TextBoxes on a Form in Excel. The tables involved are:
Customers -> CustomerId, FirstName, LastName, TelNumber
Invoice -> InvoiceId, CustomerId, CarModel, CarColor, CarPlate
Repairs -> RepairId, InvoiceId, TypeOfRepair, PartOfCar, Price
Services -> ServiceId, InvoiceId, Date, Status
When a Customer comes to the Garage, an Invoice is created, which is associated with this customer. An invoice can have many Repairs. The customer goes away without repairing the car, but the invoice is there. If the customer decides to repair the car, then a Service is created, which starts with the Status "working on it...". When the service is done, the status change to "Waiting for Check Out..."
I want to use a SQL Statement to retrieve the following Values (columns) for a specific InvoiceId:
CarModel, Color, Plate, CustomerName (FirstName LastName), PaintingTotalValue (where 'Painting' is one type in the column 'Type'), OtherTotalValue (total price of all the other types of repairs in this invoice), total price (total price, that is, painting + other ones).
I wrote the following, to get the values, but I don't know how to get the PaintingTotalValue and OtherTotalVAlue.
SELECT i.CarModel, i.Color, i.Plate, CONCAT(c.FirstName,' ',c.LastName) AS Name, FORMAT(SUM(r.Price),2) AS TotalPrice
FROM Services AS s INNER JOIN Invoices AS i ON s.invoiceId=i.invoiceId
INNER JOIN Repairs AS r ON s.invoiceId=r.invoiceId
INNER JOIN Customers AS c ON i.customerId=c.customerId
WHERE s.invoiceId = 15
Use CASE WHEN in your SELECT clause, to select the value that's conditional to the type:
SELECT
...
CASE WHEN r.Type = 'Painting' THEN r.Price ELSE 0 END PaintWorkPrice,
CASE WHEN r.Type <> 'Painting' THEN r.Price ELSE 0 END OtherWorkPrice,
FROM ...
That's one thing.
The other thing is that you're not selecting anything from the Services table, and making your query much more complicated than it needs to be.
If you can modify the schema, remove the ServiceId primary key field, and use Services.InvoiceId as a primary key instead: that will enforce the 1:1 relationship naturally.
FROM Repairs r
INNER JOIN Invoices i ON r.InvoiceId = i.InvoiceId
INNER JOIN Customers c ON i.CustomerId = c.CustomerId
The data you want to aggregate is granular to Repairs, so you select FROM that, and then move your way through the foreign keys up to Customers.
SELECT
i.CarModel
,i.Color
,i.Plate
,CONCAT(c.FirstName,' ',c.LastName) Name
,CASE WHEN r.Type = 'Painting' THEN r.Price ELSE 0 END PaintWorkPrice
,CASE WHEN r.Type <> 'Painting' THEN r.Price ELSE 0 END OtherWorkPrice
,r.Price
FROM Repairs r
INNER JOIN Invoices i ON r.InvoiceId = i.InvoiceId
INNER JOIN Customers c ON i.CustomerId = c.CustomerId
That's not aggregated yet: there's a record for each repair, for every invoice, under every customer that has an invoice. That part is the sub-query. If you have a parameter, that's where you use it.
WHERE i.InvoiceId = pInvoiceId
If you're just hard-coding an ID, that's where you do it too.
Now type SELECT q.* FROM ( on the line above, and ) q under the WHERE clause, then replace the q.* with the fields you're not aggregating - and aggregate the others. The result should be something like this:
SELECT
q.CarModel
,q.Color
,q.Plate
,q.Name
,SUM(q.PaintWorkPrice) PaintAmount
,SUM(q.OtherWorkPrice) OtherAmount
,SUM(q.Price) TotalAmount
FROM (
SELECT
i.CarModel
,i.Color
,i.Plate
,CONCAT(c.FirstName,' ',c.LastName) Name
,CASE WHEN r.Type = 'Painting' THEN r.Price ELSE 0 END PaintWorkPrice
,CASE WHEN r.Type <> 'Painting' THEN r.Price ELSE 0 END OtherWorkPrice
,r.Price
FROM Repairs r
INNER JOIN Invoices i ON r.InvoiceId = i.InvoiceId
INNER JOIN Customers c ON i.CustomerId = c.CustomerId
WHERE i.InvoiceId = 15
) q
GROUP BY
q.CarModel
,q.Color
,q.Plate
,q.Name

How to do a subquery in group_concat

I have the following data model:
`title`
- id
- name
`version`
- id
- name
- title_id
`version_price`
- id
- version_id
- store
- price
And here is an example of the data:
`title`
- id=1, name=titanic
- id=2, name=avatar
`version`
- id=1, name="titanic (dubbed in spanish)", title_id=1
- id=2, name="avatar directors cut", title_id=2
- id=3, name="avatar theatrical", title_id=2
`version_price`
- id=1, version_id=1, store=itunes, price=$4.99
- id=1, version_id=1, store=google, price=$4.99
- id=1, version_id=2, store=itunes, price=$5.99
- id=1, version_id=3, store=itunes, price=$5.99
I want to construct a query that will give me all titles that have a version_price on iTunes but not on Google. How would I do this? Here is what I have so far:
select
title.id, title.name, group_concat(distinct store order by store)
from
version inner join title on version.title_id=title.id inner join version_price on version_price.version_id=version.id
group by
title_id
This gives me a group_concat which shows me what I have:
id name group_concat(distinct store order by store)
1 Titanic Google,iTunes
2 Avatar iTunes
But how would I construct a query to include whether the item is on Google (using a case statement or whatever's needed)
id name group_concat(distinct store order by store) on_google
1 Titanic Google,iTunes true
2 Avatar iTunes false
It would basically be doing a group_concat LIKE '%google%' instead of a normal where clause.
Here's a link for a SQL fiddle of the current query I have: http://sqlfiddle.com/#!9/e52b53/1/0
Use conditional aggregation to determine if the title is in a specified store.
select title.id, title.name, group_concat(distinct version_price.store order by store),
if(count(case when store = 'google' then 1 end) >= 1,'true','false') as on_google
from version
inner join title on version.title_id=title.id
inner join version_price on version_price.version_id=version.id
group by title.id, title.name
count(case when store = 'google' then 1 end) >= 1 counts all the rows for a given title after assigning 1 to the rows which have google in them. (Or else they would be assigned null and the count ignores nulls.) Thereafter, the if checks for the countand classifies a title if it has atleast one google store on it.
http://sqlfiddle.com/#!9/b8706/2
you can just:
SELECT
title.id,
title.name,
group_concat(distinct version_price.store),
MAX(IF(version_price.store='google',1,0)) on_google
FROM version
INNER JOIN title
ON version.title_id=title.id
INNER JOIN version_price
ON version_price.version_id=version.id
GROUP BY title_id;
and add HAVING to the query if need records to be filtered:
SELECT
title.id,
title.name,
group_concat(distinct version_price.store),
MAX(IF(version_price.store='google',1,0)) on_google
FROM version
INNER JOIN title
ON version.title_id=title.id
INNER JOIN version_price
ON version_price.version_id=version.id
GROUP BY title_id
HAVING on_google;
This will give you the number of version prices not on google, and the number on google. (COUNT does not count null values.)
SELECT t.id, t.name
, COUNT(DISTINCT vpNotG.id) > 0 AS onOtherThanGoogle
, COUNT(DISTINCT vpG.id) > 0 AS onGoogle
FROM title AS t
INNER JOIN version AS v ON t.id=v.title_id
LEFT JOIN version_price AS vpNotG
ON v.id=vpNotG.version_id
AND vpNotG.store <> 'Google'
LEFT JOIN version_price AS vpG
ON v.id=vpG.version_id
AND vpG.store = 'Google'
GROUP BY t.id
or for another solution similar to vkp's:
SELECT t.id, t.name
, COUNT(DISTINCT CASE WHEN store = 'Google' THEN vp.id ELSE NULL END) AS googlePriceCount
, COUNT(DISTINCT CASE WHEN store = 'iTunes' THEN vp.id ELSE NULL END) AS iTunesPriceCount
, COUNT(DISTINCT CASE WHEN store <> 'Google' THEN vp.id ELSE NULL END) AS nonGooglePriceCount
FROM title AS t
INNER JOIN version AS v ON t.id = v.title_id
INNER JOIN version_price AS vp ON v.id = vp.version_id
GROUP BY t.id
Note: The ELSE NULL can be omitted, because if no ELSE is provided it is implied; but I included for clarity.
I would do it like below
SELECT
*
FROM
title t
INNER JOIN
version v ON
v.title_id = t.id
CROSS APPLY (
SELECT
*
FROM
version_price vp
WHERE
vp.store <> 'google'
) c ON c.version_id == v.id
Syntax may be just a little off as I can't test it right now, but I believe this is the spirit of what you would want. Cross apply is also a very efficient join which is always helpful!
This might be the most inefficient of the above answers, but the following subquery would work, using a %like% condition:
select *, case when stores like '%google%' then 1 else 0 end on_google
from (select title.id, title.name, group_concat(distinct store order by store) stores
from version inner join title on version.title_id=title.id inner join version_price
on version_price.version_id=version.id group by title_id) x
id name stores on_google
1 Titanic Google,iTunes 1
2 Avatar iTunes 0

MySQL | Sum Column If Different ID

I'm trying to Sum column only if id is different.
I tried like this, but it is not working:
SUM(CASE WHEN
DISTINCT empLeaves.ApprovedLeaveID
THEN empLeaves.TotalLeavesTaken END) AS Total
What Would be the proper way to SUM it?
Here is the total query, I am working on:
SELECT
E.employee_id AS EmployeeID,
E.employee_code AS EmployeeCode,
E.full_name AS EmployeeName,
empLeaves.LeavesFromDate,
empLeaves.LeavesToDate,
-- COUNT(empLeaves.ApprovedLeaveID) AS TotalApprovedApplications,
ATT.`in_date` AS InDate,
ATT.`out_date` AS OutDate,
SUM(CASE WHEN DISTINCT empLeaves.ApprovedLeaveID THEN empLeaves.TotalLeavesTaken END) AS Total
FROM employee E
LEFT JOIN `attendence` ATT ON ATT.`employee_id` = E.`employee_id`
LEFT JOIN (
SELECT
LA.`leave_approval_id` AS ApprovedLeaveID,
E.`employee_id` AS EmployeeID,
LA.`approved_from` AS LeavesFromDate,
LA.`approved_to` AS LeavesToDate,
DATEDIFF(LA.approved_to,approved_from) AS TotalLeavesTaken
FROM leave_application APP
INNER JOIN leave_approval LA
ON LA.`leave_application_id` = APP.`application_id` AND LA.`trashed` = 0 AND LA.`status` = 2
INNER JOIN employee E
ON E.`employee_id` = APP.`employee_id` AND E.`trashed` = 0 AND E.`enrolled` = 1
INNER JOIN ml_leave_type MLLT
ON MLLT.`ml_leave_type_id` = APP.`ml_leave_type_id` AND MLLT.`trashed` = 0
) empLeaves ON E.`employee_id` = empLeaves.EmployeeID
WHERE
E.`enrolled` = 1 AND E.`trashed` = 0
GROUP BY
E.`employee_id`,empLeaves.EmployeeID
When which ID is different from what?
In general, when you have a value in a field and you want to add them together except for those in a particular set of rows, you simply set the unwanted values to 0:
sum( case otherfield when somevalue then row.field else 0 end ) as SumOfValues
So when otherfield shows that this is the row you want included in the total, you expose the field you want to add (row.field in this example). Otherwise, you expose a zero. Of course, if the value you're testing is to find the rows you don't want in the total, just put the zero after the then and the field after the else.

sql query to select most recent record along with the empty records

I am having 2 tables..i am moving status history to one table and display it in another table..the second table fetches recent status and show it in the web page
like this i need..but i am getting as
I need the updated records along with the empty status record. My query is:
with ctr as
(
select en.status update,ed.no,row_number()
over (partition by ed.no order by en.update descending) as [RN],
ed.[conference date],ed.conference name,ed.region,ed.inquiry,
ed.attended by,ed.doctor name,ed.hospital,ed.contact no,ed.email, ed.adds,
CONVERT(VAR CHAR(10),en.[update], 103) as update,ed.closed,
CONVERT(VAR CHAR(10),ed.[closing date],103) as closing date,
ed.closed by, ed.revenue, ed.approve status, ed.Enid from inquiries_details ed
inner join
inquiries en on ed.Enid = en.Enid
inner join
staff_details SD on ed.region = SD.state
where region = 'Maharashtra'
)
select * from ctr where RN = 1
can anyone help me
Can your tried with below query, might be issue of inner join.. change it to left joins
with ctr as
(
select en.status update,ed.no,row_number()
over (partition by ed.no order by en.update descending) as [RN],
ed.[conference date],ed.conference name,ed.region,ed.inquiry,
ed.attended by,ed.doctor name,ed.hospital,ed.contact no,ed.email, ed.adds,
CONVERT(VAR CHAR(10),en.[update], 103) as update,ed.closed,
CONVERT(VAR CHAR(10),ed.[closing date],103) as closing date,
ed.closed by, ed.revenue, ed.approve status, ed.Enid from inquiries_details ed
left join
inquiries en on ed.Enid = en.Enid
left join
staff_details SD on ed.region = SD.state
where region = 'Maharashtra'
)
select * from ctr where RN = 1

SQL Query / Slow

I have the below SQL code, this is from a MySQL database. Now it gives me the results I expect, however the query is slow and I think I should speed this query up before going any further.
The table agentstatusinformation has:
PKEY (Primary Key), userid (integer), agentstate (integer)
The table axpuser contains the users name:
PKEY (Primary Key) <-- this is the key for userid, loginid (usersname)
select distinct (select loginid from axpuser where axpuser.pkey = age.userid),
case
when agentstate = 1 then 'Ready'
when agentstate = 3 then 'Pause'
end as state
from agentstatusinformation age
where (userid, pkey) in
(select userid, max(pkey) from agentstatusinformation group by userid)
I am sure this can be improved upon, but I cannot see the wood for the trees.
Many thanks.
Not precisely certain this is what you want, but I think its close:
Select loginid, case when c.agentstate=1 Then 'Ready'
when c.agentstate=3 then 'Pause'
end state
from axpuser a
join (select userid, max(pkey) pkey
from agentstatusinformation
group by userid ) b
on a.userid=b.userid
join agentstatusinformation c
and b.pkey=c.pkey
This eliminates the subselect in the initial SELECT clause, and joins against the grouped stats information table. Hope this helps.
The problem with your query are your nested selects. In particular, the subquery in the IN clause is problematic in MySQL. It gets called for every row filtered by the where clause.
The following fixes this:
select distinct (select loginid from axpuser where axpuser.pkey = age.userid),
case
when agentstate = 1 then 'Ready'
when agentstate = 3 then 'Pause'
end as state
from agentstatusinformation age
where exists (select userid, max(pkey)
from agentstatusinformation a2
where a2.userid = age.userid
group by userid
having age.pkey = max(pkey))
You can make this run faster by creating an index on agenstatusinfromation(userid, pkey).
The nested select should not be causing a problem, as long as there is an index on axpuser.pkey. However, I think it is better form to put this in the FROM clause as a join:
select distinct axpuser.loginid,
case
when agentstate = 1 then 'Ready'
when agentstate = 3 then 'Pause'
end as state
from agentstatusinformation age left outer join
axpuser
on axpuser.key = age.userid
where exists (select userid, max(pkey)
from agentstatusinformation a2
where a2.userid = age.userid
group by userid
having age.pkey = max(pkey)
)
select ax.loginid,
case
when age.agentstate = 1 then 'Ready'
when age.agentstate = 3 then 'Pause'
end as state
from
agentstatusinformation age
join
axpuser ax
on age.userid = ax.userid and age.pkey=(select max(pkey) from agentstatusinformation group by userid)