MySQL merge table, with zero vaules - mysql

Table A name is source
ID | date | valueS | commonID
1 26.8.14 Svalue01 11
2 21.8.14 Svalue02 11
3 25.8.14 Svalue03 11
Table B name is destination
ID | date | valueD | commonID
1 26.8.14 Dvalue01 11
2 21.8.14 Dvalue03 11
3 24.8.14 Dvalue03 11
So currently im using
SELECT a.*, b.* FROM (SELECT * FROM Source WHERE commonID = '11')a JOIN destination b ON a.commonID = b.commonID
But this dont get me the wished result.
i want something sorted by date, and if there is no record for both on the date, one is zero.
example how it should look
ID | date | valueD | commonID | ID | date | valueS | commonID
1 26.8.14 Dvalue01 11 1 26.8.14 Svalue01 11
3 25.8.14 Svalue03 11
3 24.8.14 Dvalue03 11
2 21.8.14 Dvalue03 11 2 21.8.14 Svalue02 11
Is and how would this be possible?
Additional Info:
-Using Mysql 5.5.37 (MariaDB)
-ID is primary on both
-date fields are "timestamp"
-value fields are INT
-ID fields are INT
-Engine is InnoDB
I hope i provided enough information and tried to make a good explained question
thank you for your help

you want to join on the date as that is the determining column so something like this
SELECT
COALESCE(s.id, "") as s_id,
COALESCE(s.date, "") as s_date,
COALESCE(s.valueS, "") as 'valueS',
COALESCE(s.commonID, "") as s_commonID,
COALESCE(d.id, "") as d_id,
COALESCE(d.date, "") as d_date,
COALESCE(d.valueD, "") as 'valueD',
COALESCE(d.commonID, "") as d_commonID
FROM source s
LEFT JOIN destination d on d.date = s.date
AND d.commonID = s.commonID
WHERE d.commonID = 11
UNION
SELECT
COALESCE(s1.id, "") as s_id,
COALESCE(s1.date, "") as s_date,
COALESCE(s1.valueS, "") as 'valueS',
COALESCE(s1.commonID, "") as s_commonID,
COALESCE(d1.id, "") as d_id,
COALESCE(d1.date, "") as d_date,
COALESCE(d1.valueD, "") as 'valueD',
COALESCE(d1.commonID, "") as d_commonID
FROM source s1
RIGHT JOIN destination d1 on d1.date = s1.date
AND d1.commonID = s1.commonID
WHERE d1.commonID = 11
ORDER BY s_date DESC, d_date DESC
DEMO

You need a Full outer Join
SELECT s.id, s.date, s.valueS, d.valueD, d.commonID FROM source s LEFT JOIN destination d ON (s.id = d.id)
UNION
SELECT s.id, s.date, s.valueS, d.valueD, d.commonID FROM source s RIGHT JOIN destination d ON (s.id = d.id);

I would go with a different solution for this problem. This starts by generating a cross product of all the common ids and dates that you want, and then using left join to bring in the other rows.
You only want one value for commonid, so this is slight overkill for your problem:
select s.*, dest.*
from (select 11 as commonid) c cross join
(select date from source union
select date from destination
) d left outer join
source s
on s.commonid = c.commonid and s.date = d.date left outer join
destination dest
on dest.commonid = c.commonid and dest.date = d.date;
But it is readily extendible. If you wanted two common ids, you could use:
select s.*, dest.*
from (select 11 as commonid union all select 12) c cross join
(select date from source union
select date from destination
) d left outer join
source s
on s.commonid = c.commonid and s.date = d.date left outer join
destination dest
on dest.commonid = c.commonid and dest.date = d.date;

Related

Query to group without lost IF function

I created a query to search for all my stock products that are in orders placed, and I created an alias "total_vendido" that adds the products when they are kits or units, so far this is ok. But now I need to group the sizes and add this "total_vendido" alias by size.
Query:
SELECT `gp`.`id`, `gp`.`data`, `gp`.`status`, `gp`.`situacao`, `gp`.`nome`,
`gp`.`razao_social`, `gp`.`email`, `gp`.`telefone`,
`itens`.*,
IF(itens.tipo = 'K',
SUM(itens.qtde_prod) * itens.qtde_lote,
SUM(itens.qtde_prod)
) AS total_vendido,
`estoq`.`titulo`
FROM `ga845_pedidos_view` `gp`
JOIN `ga845_pedido_itens` `itens` ON `itens`.`pedido_id` = `gp`.`id`
JOIN `ga845_produtos` `prod` ON `prod`.`id` = `itens`.`produtos_id`
JOIN `ga845_produtos_estoque` `estoq` ON `estoq`.`id` = `prod`.`estoques_id`
WHERE `gp`.`situacao` IN('Pedido Realizado', 'Pagamento Aprovado',
'Pedido em Separação', 'Pedido Separado')
AND date(gp.data) >= '2020-07-25'
AND date(gp.data) <= '2020-07-25'
AND `estoq`.`id` IN('24')
GROUP BY `itens`.`tamanho_prod`, `estoq`.`id`
ORDER BY `estoq`.`id` ASC, `itens`.`tamanho_prod` ASC
Current result (only important columns)
tamanho_prod | tipo | total_vendido
G | K | 5
G | U | 1
M | K | 1
P | U | 8
Expected result (only important columns)
tamanho_prod | total_vendido
G | 6
M | 1
P | 8
Code related to Expected result (only important columns)
SELECT
, `itens`.`tamanho_prod`
, SUM( IF(itens.tipo = 'K',
itens.qtde_prod * itens.qtde_lote,
itens.qtde_prod
) AS total_vendido
FROM `ga845_pedidos_view` `gp`
JOIN `ga845_pedido_itens` `itens` ON `itens`.`pedido_id` = `gp`.`id`
JOIN `ga845_produtos` `prod` ON `prod`.`id` = `itens`.`produtos_id`
JOIN `ga845_produtos_estoque` `estoq` ON `estoq`.`id` = `prod`.`estoques_id`
WHERE `gp`.`situacao` IN('Pedido Realizado', 'Pagamento Aprovado',
'Pedido em Separação', 'Pedido Separado')
AND date(gp.data) >= '2020-07-25'
AND date(gp.data) <= '2020-07-25'
AND `estoq`.`id` IN('24')
GROUP BY `itens`.`tamanho_prod`
ORDER BY `itens`.`tamanho_prod` ASC
if you want an aggregated result just for itens.tamanho_prod .. then you should use group by only for this column ... and move the SUM() outside the if condition

select by priority

Table Structure :
Registration :
uuid | name | total
Rate :
uuid | type | rate
Registration_Rate :
registration | rate
Initial Request is :
select * from registration r
join registration_rate rr on rr.registration = r.uuid
join rate rt on rt.uuid = rr.rate
group by r.name, rt.type
My SQL result from two table (registration & rate ) is :
uuid | name | rate | type
1 | AAA | 15 | U
2 | BBB | 20 | U
3 | CCC | 300 | F
4 | AAA | 250 | F
I would like to have something like this (if a rate's type 'F' exists then display instead)
uuid | name | rate | type
2 | BBB | 20 | U
3 | CCC | 300 | F
4 | AAA | 250 | F
Thanks
Edited :
I have tried another solution which works
select uuid, name, rate, (case rt.type when 2 then 2 else 1 end ) as type
from registration r
join registration_rate rr on rr.registration = r.uuid
join rate rt on rt.uuid = rr.rate
group by r.name, rt.type
If it's an F row return it. Or, use NOT EXISTS to verify no other row with same name has an F.
select t1.*
from tablename t1
where type = 'F'
or not exists (select * from tablename t2
where t2.name = t1.name
and t2.type = 'F')
Alternative solution:
select t1.*
from tablename t1
join (select name, min(type) type
from tablename
group by name) t2
ON t1.name = t2.name and t1.type = t2.type
Try this (I suggest main idea)
SELECT t.uuid,
t.name,
IFNULL(MAX(t.F_type), MAX(t.not_F_type)) AS "type",
IFNULL(MAX(t.F_rate), MAX(t.not_F_rate)) AS "rate"
FROM
(
SELECT r.uuid,
r.name,
CASE rt.type WHEN 'F' THEN rt.type END AS F_type,
CASE WHEN rt.type <> 'F' THEN rt.type END AS not_F_type,
CASE rt.type WHEN 'F' THEN rt.rate END AS F_rate,
CASE WHEN rt.type <> 'F' THEN rt.rate END AS not_F_rate
FROM registration AS r
JOIN registration_rate AS rr ON rr.registration = r.uuid
JOIN rate AS rt ON rt.uuid = rr.rate
) as t
GROUP BY t.uuid, t.name;
So, you need to split appropriate columns ("rate", "type") according to your rule (if a rate's type 'F' exists then display instead of others) into two new separate columns using case statement: the first column contains value for F type and the second one contains value for others types. I did it for "type" and "rate" columns. Then I glued together these columns (and records) using group by, aggregation functions and IFNULL statement (you can use others statement here: case, IF, etc).
As I understand the question, this is what you need.

MySQL Query To find Trains between two stations

My Train Details table,
Table Name- traindetailtb
Id Train_Number Train_Name From To
1 27658 Venad Express ABC XYZ
2 27659 Venad Express XYZ ABC
and my Train Days Table,
Table Name- traindaystb
Id Train_Number Days
1 27658 Sunday
2 27658 Wednesday
3 27659 Saturday
4 27659 Friday
and this my Train Schedule Table,
Table Name- scheduletb
Id Train_Number Station Time
1 27658 ABC 09:00am
2 27658 CDE 10:00am
3 27658 XYZ 11:00am
4 27659 XYZ 12:00pm
5 27659 CDE 01:00pm
6 27659 ABC 02:00pm
There will be two input, From and To
I need a query that gives all Trains(ie,Train_Number,Train_Name,Days) between the given stations
For eg:
For input From- CDE To-XYZ ---->
output will be-- 27658 Venad Express S W
and For input From- XYZ To-CDE ---->
output will be-- 27659 Venad Express M T
Can anyone Please help me to do this.
Are you looking for something like this?
SELECT q.train_number, MAX(t.train_name) train_name, GROUP_CONCAT(LEFT(d.days, 1)) days
FROM
(
SELECT s.train_number
FROM scheduletb s JOIN scheduletb e
ON s.train_number = e.train_number
WHERE s.station = 'CDE' -- from
AND e.station = 'XYZ' -- to
AND s.id < e.id
) q JOIN traindaystb d
ON q.train_number = d.train_number JOIN traindetailtb t
ON q.train_number = t.train_number
GROUP BY q.train_number
Output for CDE -> XYZ:
| TRAIN_NUMBER | TRAIN_NAME | DAYS |
|--------------|---------------|------|
| 27658 | Venad Express | S,W |
Output for XYZ -> CDE:
| TRAIN_NUMBER | TRAIN_NAME | DAYS |
|--------------|---------------|------|
| 27659 | Venad Express | S,F |
Here is SQLFiddle demo
You can try somthing like this:-
SELECT A.Train_number, A.Train_name, C.days
FROM traindetailtb A, traindaystb B, scheduletb C
WHERE C.Train_Number = A.Train_Number
AND A.Train_Number = B.Train_Number
AND FROM = 'ABC' AND TO = 'XYZ';
This might help you.
Try this
SELECT t.Train_Number,t.Train_Name,d.Days
from traindetailtb t,traindaystb d
WHERE
t.Train_Number=d.Train_Number AND From='ABC' AND To='XYZ';
and just read the input and put it into the from=' ' and to=' ';
this single query will return both the output?
i suggest as follow :
select
traindetailtb.Train_Number,
traindetailtb.Train_Name,
traindetailtb.From,
traindetailtb.To,
tranddaystb.Days,
scheduletb.Time
From
traindetailtb
join trandaystb on
traindetailtb.Train_Number=traindaystb.Train_Number
join scheduletb on
traindetailtb.Train_Number=scheduletb.Train_Number and
traindetailtb.From=scheduletb.From
where
traindetailtb.From=$inputFrom
and
traindetailtb.To=$inputTo
Using a self join you can do this, assuming that the Time is really a time field
SELECT a.Train_Number, d.Train_Name, GROUP_CONCAT(e.Days)
FROM scheduletb a
INNER JOIN scheduletb b
ON a.Train_Number = b.Train_Number
AND b.Time > a.Time
INNER JOIN traindetailtb d
ON a.Train_Number = d.Train_Number
INNER JOIN traindaystb e
ON a.Train_Number = e.Train_Number
WHERE a.Station = 'CDE'
AND b.Station = 'XYZ'
GROUP BY a.Train_Number, d.Train_Name
assuming that you have a real time field for the time
If you wanted only the trains that went directly from CDE to XYZ with no intermediate stations you could add a LEFT OUTER JOIN to the table looking for stations with a time between the other 2:-
SELECT a.Train_Number, d.Train_Name, GROUP_CONCAT(e.Days)
FROM scheduletb a
INNER JOIN scheduletb b
ON a.Train_Number = b.Train_Number
AND b.Time > a.Time
LEFT OUTER JOIN scheduletb c
ON a.Train_Number = c.Train_Number
AND c.Time > a.Time
AND c.Time < b.Time
INNER JOIN traindetailtb d
ON a.Train_Number = d.Train_Number
INNER JOIN traindaystb e
ON a.Train_Number = e.Train_Number
WHERE a.Station = 'CDE'
AND b.Station = 'XYZ'
AND c.Station IS NULL
GROUP BY a.Train_Number, d.Train_Name
If you want to visualize the days in one single cell instead of spread into multiple rows, use GROUP_CONCAT, which acts as a aggregator-function.
select tde.train_number, tde.train_name, GROUP_CONCAT(tda.days, ",") as days
from traindetailtb tde
join traindaystb tda on tda.train_number=tde.train_number
where tde.from = $user_input_from
and tde.to = $user_input_to
group by tde.train_number, tde.train_name

MySQL: Add a "Total" Count to the last row of SQL Query for Unique value only

Info: Server version: 5.1.39 / MySQL / phpMyAdmin
Php: 5.4
Server: Apache
Code is run via: Server SQL Query (copy & paste in the phpMyAdmin) or in MySQL Workbench or using a custom shopping cart manager.
Exports to: Excel (.csv then to .xlsx for sales reports)
Other: I do use a number of tables for referencing
Question
I want to add a sub-total to the bottom (or top) row of my SQL Query. I am wanting to count Unique Order numbers only. Either as a whole, or by my date query.
This works, but puts it in 1 row, 1 column then does not generate the rest of my query.
COUNT( DISTINCT( T5.orders_id ) ) As OrdUnique,
Returns:
OrdUnique | OrdID | ProdName | etc
2342 | 21 | Name | Rest of data
What I would like is:
OrdID | ProdName | Qty | etc
2525 | prod | 1 |
2538 | prod | 1 |
2553 | prod | 1 |
2553 | prod | 1 |
2538 | prod | 1 |
OrdUnq = 3
The basic structure of my existing code is:
Select
T5.orders_id As OrdID,
T3.products_name As ProdName,
T2.products_quantity As Qty,
more content
even more content (about 70 lines of query)
ends with (similar)
From /*PREFIX*/products T1
Left Join /*PREFIX*/orders_products T2 On (T1.products_id = T2.products_id)
Inner Join /*PREFIX*/orders T5 On (T5.orders_id = T2.orders_id)
Left Join /*PREFIX*/manufacturers T4 On (T1.manufacturers_id = T4.manufacturers_id)
Where (T5.date_purchased >= 20120101) And (T5.date_purchased <= 20131216) And T5.orders_status = x
Order By T5.orders_id
Notes:
I do not run this via PHP, it is simply a copy & paste from my .sql/.txt file in to the backend of my server OR through MySQL Workbench
Throws a table access error
(select COUNT( DISTINCT( T5.orders_id ) ) from T5.orders) As OrdUnique,
Throws a Error Code: 1242: subquery returns more than one row
(select COUNT( DISTINCT( T5.orders_id ) ) as OrdUnq FROM orders GROUP BY orders_id WITH ROLLUP),
(as seen here: Calculate the total time duration on last row in mysql)
This also does not work:
Count unique records in database
Thank you in advance for your insight.
I know it is not very efficient, but an easy solution woudl be to use this query:
SELECT null as total,
T5.orders_id As OrdID,
T3.products_name As ProdName,
T2.products_quantity As Qty
From /*PREFIX*/products T1
Left Join /*PREFIX*/orders_products T2 On (T1.products_id = T2.products_id)
Inner Join /*PREFIX*/orders T5 On (T5.orders_id = T2.orders_id)
Left Join /*PREFIX*/manufacturers T4 On (T1.manufacturers_id = T4.manufacturers_id)
Where (T5.date_purchased >= 20120101)
And (T5.date_purchased <= 20131216)
And T5.orders_status = 'x'
Order By T5.orders_id
UNION
SELECT count(*) AS total,
null As OrdID,
null As ProdName,
null As Qty
FROM (select T5.orders_id
From /*PREFIX*/products T1
Left Join /*PREFIX*/orders_products T2 On (T1.products_id = T2.products_id)
Inner Join /*PREFIX*/orders T5 On (T5.orders_id = T2.orders_id)
Left Join /*PREFIX*/manufacturers T4 On (T1.manufacturers_id = T4.manufacturers_id)
Where (T5.date_purchased >= 20120101)
And (T5.date_purchased <= 20131216)
And T5.orders_status = 'x'
GROUP BY T5.orders_id
) as s
Pay attention to UNION and GROUP BY.

How to query and group every continuous number series in MySQL?

I have this freight.or_nos table which contains series of receipt numbers. I want to list all the or's being issued excluding the status='Cancelled' making the series broken in groups.
For example I have this receipt stab 125001-125050, and 125020 is cancelled so the listing result would be:
+-------------------------------------------------------+
| OR Start | OR End | Quantity | Amount |
+-------------------------------------------------------+
| 125001 | 125019 | 19 | |
+-------------------------------------------------------+
| 125021 | 125050 | 30 | |
+-------------------------------------------------------+
This seems to be a tough query.
Thanks for reading but I already made it, just now! :)
Here's my query(disregard the other characters it's form our CGI):
{.while SELECT `start`,`end`,or_prefix,or_suffix,SUM(a.amount) AS g_total,COUNT(*) AS qcount FROM (SELECT l.id AS `start`,( SELECT MIN(a.id) AS id FROM ( SELECT a.or_no AS id FROM freight.`or_nos` a WHERE a.status!='Cancelled' AND a.log_user = 0#user_teller AND DATE(a.or_date)='#user_date`DATE' AND IF(a.status='Default' AND a.amount=0,0,1) ) AS a LEFT OUTER JOIN ( SELECT a.or_no AS id FROM freight.`or_nos` a WHERE a.status!='Cancelled' AND a.log_user = 0#user_teller AND DATE(a.or_date)='#user_date`DATE' AND IF(a.status='Default' AND a.amount=0,0,1) ) AS b ON a.id = b.id - 1 WHERE b.id IS NULL AND a.id >= l.id ) AS `end` FROM ( SELECT a.or_no AS id FROM freight.`or_nos` a WHERE a.status!='Cancelled' AND a.log_user = 0#user_teller AND DATE(a.or_date)='#user_date`DATE' AND IF(a.status='Default' AND a.amount=0,0,1) ) AS l LEFT OUTER JOIN ( SELECT a.or_no AS id FROM freight.`or_nos` a WHERE a.log_user = 0#user_teller AND DATE(a.or_date)='#user_date`DATE' AND IF(a.status='Default' AND a.amount=0,0,1) ) AS r ON r.id = l.id - 1 WHERE r.id IS NULL) AS k LEFT JOIN freight.`or_nos` a ON a.`or_no` BETWEEN k.start AND k.end AND DATE(a.`or_date`)='#user_date`DATE' AND a.log_user =0#user_teller AND IF(a.status='Default' AND a.amount=0,0,1) AND a.status!='Cancelled' GROUP BY `start`}
{.start}{.x.24.12:end}{.x`p0.40.-5:qcount}{.x`p2.57.-15:g_total}{.asc 255}
{.wend}{.asc 255}