How to avoid two results of the same row in join - mysql

I have three tables actually on virturt mart table one is orders, another is item & one is order_user_info
to get the user first name i need to join order_user_info table
but when i join it shows the result info double, below i have mentioned the query & result please guide how can avoid double result
*FOR JOIN FIRST NAME I AM USING BELOW MENTIONED QUERY *
LEFT JOIN `urbanite_virtuemart_order_userinfos` as Uinfo ON Uinfo.virtuemart_order_id=i.virtuemart_order_id
*COMPLETE QUERY *
SELECT SQL_CALC_FOUND_ROWS o.created_on AS intervals, CAST( i.`created_on` AS DATE ) AS created_on, Uinfo.`first_name`, o.`order_number`, SUM(DISTINCT i.product_item_price * product_quantity) as order_subtotal_netto, SUM(DISTINCT i.product_subtotal_with_tax) as order_subtotal_brutto, COUNT(DISTINCT i.virtuemart_order_id) as count_order_id, SUM(i.product_quantity) as product_quantity FROM `urbanite_virtuemart_order_items` as i
LEFT JOIN `urbanite_virtuemart_orders` as o ON o.virtuemart_order_id=i.virtuemart_order_id
LEFT JOIN `urbanite_virtuemart_order_userinfos` as Uinfo ON Uinfo.virtuemart_order_id=i.virtuemart_order_id AND Uinfo.created_on = i.created_on AND Uinfo.virtuemart_user_id = o.virtuemart_user_id
WHERE (`i`.`order_status` = "S") AND i.virtuemart_vendor_id = "63" AND DATE( o.created_on ) BETWEEN "2013-06-01 05:00:00" AND "2013-06-30 05:00:00"
GROUP BY intervals
ORDER BY created_on DESC LIMIT 0, 400
result i am getting with out join like below
intervals | Created_on | order_no | order_subtotalnetto | order_subtotalbruto | count_order_id | product_quantity
2013-06-12 09:47:16 |2013-06-12 | 43940624 | 200.00000 | 200.00000 | 1 | 2
result i am getting with join for firstname like below
intervals | Created_on | order_no | f_name | order_subtotalnetto | order_subtotalbruto | count_order_id | product_quantity
2013-06-12 09:47:16 |2013-06-12 | Fatin Bokhari | 43940624 | 200.00000 | 200.00000 | 1 | 4
see in with out join for first name it show product_quantity = 2 but when i join it shows the value double, i tried distinct but cant go this way as it show the product quantity = 1 every time
Kindly need rescue!

oh actually the rows comes twice in a urbanite_virtuemart_order_userinfos table so i used where clause & it works
WHERE (`i`.`order_status` = "S") AND i.virtuemart_vendor_id = "63" AND DATE( o.created_on ) BETWEEN "2013-06-01 05:00:00" AND "2013-06-30 05:00:00" AND Uinfo.`address_type` = 'BT'

Related

MySQL script triggers Error #1241

I have the script below, which is supposed to get a price for an array of ID's that has been provided.
But it needs to get 1 price per ID, and the tricky part is, that I want to have the ability to have scheduled price updates.
This mean that it needs to take the price that is <= UTC_TIMESTAMP smaller or equal to the current time.
SELECT
`product_pricing`.`wo_id`,
`product_pricing`.`currency` AS price2_currency,
`product_pricing`.`price` AS price2,
`product_pricing`.`formula_id`,
`product_pricing`.`vat_calculated` AS price2_vat_calculated,
`product_pricing`.`vat_id`,
`product_pricing`.`timestamp_valid`,
`product_price_formulas`.`formula_id`,
`product_price_formulas`.`formula` price2_formula
FROM
`product_pricing`
LEFT JOIN `product_price_formulas`
ON `product_pricing`.`formula_id` = `product_price_formulas`.`formula_id`
WHERE
`product_pricing`.`wo_id` IN (
SELECT
`product_pricing`.`wo_id`,
`product_pricing`.`timestamp_valid`
FROM `product_pricing`
WHERE
`product_pricing`.`wo_id`
IN ('015724', '028791', '015712', '015715', '015717', '039750', '028791')
AND `product_pricing`.`timestamp_valid` <= UTC_TIMESTAMP
ORDER BY `product_pricing`.`timestamp_valid` DESC
)
Is this possible?
Sample data: Current output
——————————————————————————————————————————————————————————————————
| wo_id | price2 | timestamp_valid
——————————————————————————————————————————————————————————————————
| 028791 | 8000 | 2018-03-20 19:55:41
| 028791 | 6000 | 2018-04-01 19:55:41
| 028791 | 4000 | 2018-04-20 19:55:41
| 015724 | 3000 | 2018-04-18 19:55:41
| 015724 | 1500 | 2018-03-01 19:55:41
....
Wanted output:
——————————————————————————————————————————————————————————————————
| wo_id | price2 | timestamp_valid
——————————————————————————————————————————————————————————————————
| 028791 | 6000 | 2018-04-01 19:55:41
| 015724 | 1500 | 2018-03-01 19:55:41
I guess your issue is on the IN clause.
You select two field in IN clause.
EDIT
You need to self join a subquery by wo_id and Max timestamp_valid.
SELECT
`product_pricing`.`wo_id`,
`product_pricing`.`currency` AS price2_currency,
`product_pricing`.`price` AS price2,
`product_pricing`.`formula_id`,
`product_pricing`.`vat_calculated` AS price2_vat_calculated,
`product_pricing`.`vat_id`,
`product_pricing`.`timestamp_valid`,
`product_price_formulas`.`formula_id`,
`product_price_formulas`.`formula` price2_formula
FROM
`product_pricing`
LEFT JOIN `product_price_formulas` ON `product_pricing`.`formula_id` = `product_price_formulas`.`formula_id`
INNER JOIN
(
SELECT
`product_pricing`.`wo_id`,
MAX(`timestamp_valid`) AS MaxDate
FROM `product_pricing`
WHERE
`product_pricing`.`timestamp_valid` <= UTC_TIMESTAMP
GROUP BY
`product_pricing`.`wo_id`
)as temp ON temp.wo_id = `product_pricing`.`wo_id` AND temp.MaxDate = `product_pricing`.`timestamp_valid`
WHERE
`product_pricing`.`wo_id` IN ('015724', '028791', '015712', '015715', '015717', '039750', '028791')
I think you want something like this:
SELECT pp.*, ppf.formula_id, ppf.formula as price2_formula
FROM product_pricing pp LEFT JOIN
product_price_formulas ppf
ON pp.formula_id = ppf.formula_id
WHERE (pp.wo_id, pp.timestamp_valid) IN
(SELECT pp2.wo_id, MAX(pp2.timestamp_valid)
FROM product_pricing pp2
WHERE pp2.wo_id IN ('015724', '028791', '015712', '015715', '015717', '039750', '028791') AND
pp2.timestamp_valid <= UTC_TIMESTAMP
);
The ORDER BY makes no sense in the subquery, so this is my best guess as to what you want.
I left this with your structure of using IN, but I would use a correlated subquery and =:
WHERE pp.timestamp_valid = (SELECT MAX(pp2.timestamp_valid)
FROM product_pricing pp2
WHERE pp2.wo_id = pp.wo_id AND
pp2.timestamp_valid <= UTC_TIMESTAMP
) AND
pp2.wo_id IN ('015724', '028791', '015712', '015715', '015717', '039750', '028791');
You could JOIN product_pricing with a derived result set of the most recent valid_timestamp records.
If we ignore the product_pricing_formula table for now (since you didn't include it in your specimen data and result) that would give.
SELECT
p.`wo_id`,
p.`price` AS price2,
p.`timestamp_valid`
FROM `product_pricing` p
JOIN (SELECT wo_id, MAX(timestamp_valid) AS max_valid_ts
FROM `product_pricing`
WHERE `timestamp_valid` <= UTC_TIMESTAMP
GROUP BY wo_id) d
ON (d.wo_id = p.wo_id AND d.max_valid_ts = p.timestamp_valid)
WHERE p.`wo_id` IN (015724, 028791);
Try it on Sqlfiddle

PHP MySQL SELECT newest entry grouped by an ID and ordered by timestamp

I am working on an problem regarding Selecting data from two MySQL tables.
First table holds messages | messages | (id, msg_group_id, to_user_id, from_user_id, datetime)
Second table holds user data | profiles | (user_id, name, firstname, ...)
ATM it works the way, that I can select ALL messages with a certain 'to_id' and by adding a JOIN statement getting the name and firstname of the user who sends the message.
My problem now is that I can not figure out a way to ONLY select the newest message of a certain msg_group_id.
I already tried GROUP BY msg_group_id combined with ORDER BY datetime DESC.
But that only throws the very first entry in message table. But I want to last one. :-)
I hope you can help me. :-)
My actual SQL statement:
SELECT LEFT(messages.message, 10) AS message,
`messages`.`msg_group_id`,
`messages`.`datetime`,
`profiles`.`name`,
`profiles`.`firstname`
FROM `messages`
LEFT JOIN `profiles`
ON `messages`.`from_user_id` = `profiles`.`user_id`
WHERE `to_user_id` = '2'
ORDER BY `datetime` DESC
LIMIT 20;
Thanks in Advance
Sample INPUT:
[messages]
|id|msg_group_id|to_user_is|from_user_id|message |datetime|
0 | 1 | 1 | 2 | Hello World1 | 2015-12-21 10:42:00
1 | 1 | 1 | 2 | Hello World2 | 2015-12-21 10:43:00
2 | 1 | 1 | 2 | Hello World3 | 2015-12-21 10:44:00
[profiles]
user_id|name |firstname|
1 | Test | User
2 | Thanks | Worldname
Result (what I don't want)
message|msg_group_id|datetime|name|firstname
Hello World1 | 1 | 2015-12-21 10:42:00 | Thanks | Worldname
Result (what I want)
message|msg_group_id|datetime|name|firstname
Hello World3 | 1 | 2015-12-21 10:44:00 | Thanks | Worldname
May be this query can help:
SELECT m.message, m.msg_group_id, m.datetime, u.name, u.firstname
FROM message as m, profiles as u
WHERE m.from_user_id = u.user_id
GROUP BY m.msg_group_id
ORDER BY m.datetime DESC
Or use INNER JOIN
SELECT m.message, m.msg_group_id, m.datetime, u.name, u.firstname
FROM message as m
INNER JOIN profiles as u ON m.from_user_id = u.user_id
GROUP BY m.msg_group_id
ORDER BY m.datetime DESC
I guess I solved the Problem with the help of another thread:
https://stackoverflow.com/a/1313140/4493030
My SQL Statement as follows:
SELECT `messages`.*, `profiles`.`nick_name`
FROM `messages`
LEFT JOIN `profiles`
ON `messages`.`from_user_id` = `profiles`.`user_id`
INNER JOIN
(SELECT konversation_id, MAX(id) AS maxid FROM messages
WHERE messages.to_user_id = 2
GROUP BY konversation_id) AS b
ON messages.id = b.maxid
WHERE `to_user_id` = '2'
ORDER BY `datetime` DESC
LIMIT 20;
Thanks to all of you who tried to help.
I found a way to tight it down
SELECT messages.to_user_id, messages.msg_group_id, MAX(messages.id) AS maxid, messages.from_user_id, profiles.name
FROM messages
LEFT JOIN profiles
ON messages.from_user_id = profiles.user_id
WHERE messages.to_user_id = 2
GROUP BY msg_group_id

Select values based on other value within joined tables SQL

I would like to ask for help for an SQL request that give me values from two tables.
As an example I have one Table orders und one table processing.
I would like to make an report of the orders and the state of processing.
table orders
id | status | div
-------------------
1 | wating_r | div1
2 | closed | div2
3 | closed | div3
-
table processing:
id | order_id | type | date
----------------------------------------
1 | 2 | send_request | 15.01.15
2 | 2 | send_invoice | 30.01.15
3 | 1 | send_request | 01.02.15
4 | 3 | send_request2 | 10.02.15
5 | 3 | send_invoice | 15.02.15
what I would like to get:
order_id | status | date_request | date_request2 | date_invoice
--------------------------------------------------------------------------------
1 | waiting_r | 01.02.15 | NULL | NULL
2 | closed | 15.01.15 | NULL | 30.01.15
3 | closed | NULL | 10.02.15 | 15.02.15
my solution:
select orders.id as order_id, orders.status, IF(processing.type='send_invoice',date_format(processing.date, '%Y-%m-%d'), NULL) as date_invoice, IF(processing.type='send_request',date_format(processing.date, '%Y-%m-%d'), NULL) as date_request, IF(processing.type='send_request2',date_format(processing.date, '%Y-%m-%d'), NULL) as date_request2
from orders
inner join processing on orders.id = processing.order_id
where
case
when orders.status='closed' then processing.type='send_invoice'
when orders.status='waiting_r' then processing.type='send_request'
when orders.status='waiting_2'then processing.type='send_request2'
end
This works fine but with this IF statements I doesn't become the dates from the requests when an invoice was sent - I only get the date of the invoice.
Instead of the case request I tried the following but in this case I have more than one line for every order. When I tried to "group by" I have mixed data.
where
processing.type in ('send_invoice', 'send_request', 'completion_request_send')
You need to left-join the second table to the first three times, like so.
SELECT o.id AS order_id, o.status,
p1.date AS date_request,
p2.date AS date_request2,
p3.date AS date_invoice
FROM orders o
LEFT JOIN processing p1 ON o.id = p1.order_id AND p1.type='send_request'
LEFT JOIN processing p2 ON o.id = p2.order_id AND p2.type='send_request2'
LEFT JOIN processing p3 ON o.id = p3.order_id AND p3.type='send_invoice'
ORDER BY 1,2
This left-join with an id-matching criterion and the specific type choice pulls out the rows you need for each column. Left, as opposed to inner, join, allows the missing values to be shown as null.
Here it is, working. http://sqlfiddle.com/#!9/b8c74/5/0
This is a typical pattern for joining a key/value table where the (id/key) pairs are unique.
Edit Unfortunately it generates duplicate result set rows in situations where there's a duplicate key for a particular value. To deal with that, it's necessary to deduplicate the key/value table (processing) in this case.
This subquery will do that, taking the latest date value.
SELECT type, order_id, MAX(date) AS date
FROM processing
GROUP BY type, order_id
Then you have to use that subquery in the main query. This is where it would be good if MySQL had common table expressions. But it doesn't so things get kind of verbose.
SELECT o.id AS order_id, o.status,
p1.date AS date_request,
p2.date AS date_request2,
p3.date AS date_invoice
FROM orders o
LEFT JOIN (
SELECT type, order_id, MAX(date) AS date
FROM processing
GROUP BY type, order_id
) p1 ON o.id = p1.order_id AND p1.type='send_request'
LEFT JOIN (
SELECT type, order_id, MAX(date) AS date
FROM processing
GROUP BY type, order_id
) p2 ON o.id = p2.order_id AND p2.type='send_request2'
LEFT JOIN (
SELECT type, order_id, MAX(date) AS date
FROM processing
GROUP BY type, order_id
) p3 ON o.id = p3.order_id AND p3.type='send_invoice'
ORDER BY 1,2

Count new and returning items by year

Following up on the question "Select new or returning items for a specified year", I would now like to create statistics on how many items per year are new or returning. Here's my example table:
+---------------------+----------+
| date | item |
+---------------------+----------+
| 2008-11-30 11:15:59 | Plums |
| 2012-11-08 19:42:37 | Lemons |
| 2013-01-30 18:58:07 | Apples |
| 2013-02-12 13:44:45 | Pears |
| 2014-06-08 11:46:48 | Apples |
| 2014-09-01 20:28:03 | Oranges |
+---------------------+----------+
I'm looking for a query which will return the amount of items that have not appeared in previous years, and the amount of items that have already appeared in previous years.
The result should look something along the lines of this:
Year New Items Returning Items
2008 1 0
2012 1 0
2013 2 0
2014 1 1
How can this be achieved?
Well, using the queries you are linking to, I came up with the following (SQL Fiddle):
SELECT m1.UniqYear,
CASE WHEN m2.NewItems IS NULL THEN 0 ELSE m2.NewItems END AS NewItems,
CASE WHEN m3.ReturningItems IS NULL THEN 0 ELSE m3.ReturningItems END AS ReturningItems
FROM
(
SELECT DISTINCT YEAR(s.date) AS UniqYear
FROM MyTable s
) m1 LEFT JOIN
(
SELECT YEAR(s1.date) AS Year, COUNT(*) AS NewItems
FROM MyTable s1
LEFT JOIN MyTable s2 ON s1.item = s2.item AND YEAR(s2.date) < YEAR(s1.date)
WHERE s2.date IS NULL
GROUP BY YEAR(s1.date)
) m2 ON m1.UniqYear = m2.Year
LEFT JOIN
(
SELECT YEAR(s3.date) AS Year, COUNT(*) AS ReturningItems
FROM MyTable s3
INNER JOIN MyTable s4 ON s3.item = s4.item
WHERE YEAR(s4.date) < YEAR(s3.date)
GROUP BY YEAR(s3.date)
) m3 ON m1.UniqYear = m3.Year
ORDER BY m1.UniqYear;
I am using the CASE statements to return 0 instead off null. The first sub query (m1) is used to get the distinct years. The second sub query (m2) is used to get the New Items count. The third sub query (m3) is used to get the Returning Items count. Notice I had to add a group by statement to get the count per year. I had to LEFT JOINed m2 and m3 to m1 because you are not always going to have a NewItems count or a Returning Items for every year.

difficulties getting a 3 table join to return expected results

I'm having some difficulty getting to the bottom of this sql query.
Tables:
--Tickets-- --Finance-- --Access--
id_tickets id_finance id_access
name_tickets id_event id_event
cat_tickets id_tickets id_tickets
sold_finance scan_access
Finance and Access both contain a row for multiple of each ticket type as listed in tickets.
and I'm trying to get:
cat_tickets | total_sold | total_scan
-------------------------------------
single | 3043 | 2571
season | 481 | 292
comp | 114 | 75
-------------------------------------
total | 3638 | 2938
The closest I've been to the result I've used:
SELECT tickets.cat_tickets, COALESCE(SUM(finance.sold_finance), 0) AS total_sold, COALESCE(SUM(access.scan_access), 0) AS total_scan
FROM finance INNER JOIN tickets ON finance.id_tickets = tickets.id_tickets
INNER JOIN access ON access.id_tickets = tickets.id_tickets
WHERE access.id_event = 235 AND finance.id_event = access.id_event
GROUP BY tickets.cat_tickets
ORDER BY tickets.cat_tickets DESC
but that just returns:
cat_tickets | total_sold | total_scan
-------------------------------------
single | 4945 | 4437
season | 954 | 599
comp | 342 | 375
-------------------------------------
total | 6241 | 5411
Any ideas where I could be going wrong?
Thanks!
The problem is the relation between access and finance tables, you have to join them. Even if you LEFT JOIN the table the predicate finance.id_event = access.id_event will make it INNER JOIN. As a work around, use UNION like this:
SELECT
tickets.cat_tickets,
SUM(CASE WHEN a.Type = 'f' THEN num ELSE 0 END) AS total_sold,
SUM(CASE WHEN a.Type = 'a' THEN num ELSE 0 END) AS total_scan
FROM tickets
LEFT JOIN
(
SELECT 'f' Type, id_tickets, sold_finance num
FROM finance f
WHERE id_event = 1
UNION ALL
SELECT 'a', id_tickets, scan_access
FROM access
WHERE id_event = 1
) a ON a.id_tickets = tickets.id_tickets
GROUP BY tickets.cat_tickets;
SQL Fiddle Demo
Although I am fully clear on what you want, just try this query if the result of this is what you are expecting.
SELECT tickets.cat_tickets, COALESCE(SUM(finance.sold_finance), 0) AS total_sold, COALESCE(SUM(access.scan_access), 0) AS total_scan
FROM finance LEFT JOIN tickets ON finance.id_tickets = tickets.id_tickets
LEFT JOIN access ON access.id_tickets = tickets.id_tickets
WHERE access.id_event = 235
GROUP BY tickets.cat_tickets
ORDER BY tickets.cat_tickets DESC
Disclaimer: This query is not tested due to incomplete data on the question.
SELECT z.Cat_tickets,
COALESCE(x.total_sold,0) total_sold,
COALESCE(y.total_scan,0) total_scan
FROM tickets z
LEFT JOIN
(
SELECT a.id_tickets,
a.cat_tickets,
SUM(b.sold_finance) total_sold
FROM tickets a
INNER JOIN finance b
ON a.id_tickets = b.id_tickets
WHERE id_event = 235
GROUP BY a.id_tickets, a.cat_tickets
) x ON z.id_tickets = x.id_tickets
LEFT JOIN
(
SELECT aa.id_tickets,
aa.cat_tickets,
SUM(bb.scan_access) total_scan
FROM tickets aa
INNER JOIN Access bb
ON aa.id_tickets = bb.id_tickets
WHERE id_event = 235
GROUP BY aa.id_tickets, aa.cat_tickets
) y ON z.id_tickets = y.id_tickets