MySQL group by - union all - mysql

I have this query for a report:
select #t := '' as 'Clave', #tf:='Inventario Físico' as 'Descripción', #t:= '' as 'Cantidad', #t:= '' as 'Precio Unitario' union all
select #t:= '', #t:= '', #t:= '', #t:= '' union all
(select cla, des, can, CAST(pl1*can as Decimal(10,2)) from inventario order by cla) union all
select #t:= '', #t:='', #tnde := 'Número de Elementos: ', count(*) from inventario union all
select #t:= '', #t:= '', #tne:= 'Suma total: $', sum(ppu) from inventario;
I need an "order by" for the 3rd query.
select cla, des, can, CAST(pl1*can as Decimal(10,2)) from inventario order by cla
By itself, that line of code works perfectly, but, when it's between the unions, all the info it is not ordered. How can I solve this? Thanks.

union all does not guarantee that the data is in the order specified by the subqueries. You need to do an explicit order by to get that result.
This approach adds an ordering column to keep the groups together. The final order by clause first orders by ordering and then by the column used for ordering the third subquery:
(select #t := '' as Clave, #tf:='Inventario Físico' as Descripción,
#t:= '' as "Cantida", #t:= '' as "Precio Unitario", 0 as ordering
) union all
(select #t:= '', #t:= '', #t:= '', #t:= '', 1 as ordering) union all
(select cla, des, can, CAST(pl1*can as Decimal(10,2)), 2 from inventario) union all
(select #t:= '', #t:='', #tnde := 'Número de Elementos: ', count(*), 3 from inventario) union all
(select #t:= '', #t:= '', #tne:= 'Suma total: $', sum(ppu), 4 from inventario)
order by ordering, clave;
I also changed the single quotes on the column aliases to double quotes. I think it is good practice to only use single quotes for string constants.

Related

How to search all table records and if not exist a record create a new empty?

I have a table with year and monthly sales, but some months doesn't have sales and i need to run all table and if doesn't exist create one with zero sales.
This is my database structure
CREATE TABLE `MonthlySales` (
`code` int(8) NOT NULL,
`Year` smallint(4) NOT NULL,
`Month` tinyint(2) NOT NULL,
`SalesQty` varchar(12) NOT NULL,
`SalesValue` varchar(12) NOT NULL,
`TValue` varchar(12) NOT NULL,
`DValue` varchar(12) NOT NULL,
`CValue` varchar(12) NOT NULL
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
ADD PRIMARY KEY (`code`,`Year`,`Month`),
ADD UNIQUE KEY `Article` (`code`,`Year`,`Month`);
INSERT INTO `MonthlySales` (`code`, `Year`, `Month`, `SalesQty`, `SalesValue`, `TValue`, `DValue`, `CValue`) VALUES
(100000, 2017, 1, '1', '1.6', '', '', '1.11'),
(100000, 2017, 2, '1', '1.6', '', '', '1.11'),
/*Here i need new records with zero values for months 3,4,5,6,7*/
(100000, 2017, 7, '98', '144.2', '', '29.76', '108.78'),
(100000, 2017, 8, '124', '191.65', '', '74.4', '137.64'),
/*Here i need new records with zero values for months 9,10,11*/
(100000, 2017, 12, '7', '11.2', '', '', '7.78'),
(100000, 2018, 1, '2', '3.2', '', '', '2.24'),
(100000, 2018, 3, '4', '6.32', '', '', '4.44'),
(100000, 2018, 4, '8', '12.8', '', '', '8.91'),
(100000, 2018, 9, '18', '28.74', '', '19.2', '19.84'),
(100000, 2018, 10, '19', '30.02', '', '14.22', '20.97'),
(100000, 2018, 11, '2', '3.16', '', '', '2.2'),
(100000, 2018, 12, '2', '3.16', '', '', '2.2'),
(100000, 2019, 1, '14', '22.12', '', '', '15.38'),
(100000, 2019, 2, '8', '12.64', '', '', '8.8'),
(100000, 2019, 6, '47', '74.26', '', '', '51.7'),
(100002, 2017, 1, '14', '54.02', '', '', '41.16'),
(100002, 2017, 2, '12', '46.8', '', '', '35.28'),
/*Here i need new records with zero values for months 3,4 */
(100002, 2017, 5, '20', '78', '', '', '58.8'),
(100002, 2017, 6, '14', '49.92', '', '', '41.16'),
I need for for all year and months from 2017 until now.
We can write a query that generates all of the rows that we want to return. We can use a semi-Cartesian product.
We can start with this to return the year values:
SELECT y.yyyy
FROM ( SELECT 2017 AS `yyyy` UNION ALL SELECT 2018 UNION ALL SELECT 2019 ) y
ORDER
BY y.yyyy
And then add a join to another row source to add month values for each year. We can also add a filter so that rows that have year and month values from future dates are excluded.
Something like this:
SELECT y.yyyy
, m.mm
FROM ( SELECT 2017 AS `yyyy` UNION ALL SELECT 2018 UNION ALL SELECT 2019 ) y
CROSS
JOIN ( SELECT 1 AS mm
UNION ALL SELECT 2 UNION ALL SELECT 3 UNION ALL SELECT 4
UNION ALL SELECT 5 UNION ALL SELECT 6 UNION ALL SELECT 7
UNION ALL SELECT 8 UNION ALL SELECT 9 UNION ALL SELECT 10
UNION ALL SELECT 11 UNION ALL SELECT 12
) m
HAVING CONCAT(y.yyyy,'-',m.mm,'-01') < DATE_FORMAT(NOW(),'%Y-%m-01')
ORDER
BY y.yyyy
, m.mm
And if we need to generate all of those same rows multiple times, once for each of a given set of code values, we can add another join for another semi-Cartesian product:
SELECT c.code
, y.yyyy
, m.mm
FROM ( SELECT 2017 AS `yyyy` UNION ALL SELECT 2018 UNION ALL SELECT 2019 ) y
CROSS
JOIN ( SELECT 1 AS mm
UNION ALL SELECT 2 UNION ALL SELECT 3 UNION ALL SELECT 4
UNION ALL SELECT 5 UNION ALL SELECT 6 UNION ALL SELECT 7
UNION ALL SELECT 8 UNION ALL SELECT 9 UNION ALL SELECT 10
UNION ALL SELECT 11 UNION ALL SELECT 12
) m
CROSS
JOIN ( SELECT 100000 AS code
UNION ALL SELECT 100002
) c
HAVING CONCAT(y.yyyy,'-',m.mm,'-01') < DATE_FORMAT(NOW(),'%Y-%m-01')
ORDER
BY c.code
, y.yyyy
, m.mm
If table MonthlySales contains matching rows, and we want to identify rows that don't have a matching row in MonthlySales we could do a conditional test with a NOT EXISTS on a correlated subquery, or an anti-join pattern
SELECT c.code
, y.yyyy
, m.mm
FROM ( SELECT 2017 AS `yyyy` UNION ALL SELECT 2018 UNION ALL SELECT 2019 ) y
CROSS
JOIN ( SELECT 1 AS mm
UNION ALL SELECT 2 UNION ALL SELECT 3 UNION ALL SELECT 4
UNION ALL SELECT 5 UNION ALL SELECT 6 UNION ALL SELECT 7
UNION ALL SELECT 8 UNION ALL SELECT 9 UNION ALL SELECT 10
UNION ALL SELECT 11 UNION ALL SELECT 12
) m
CROSS
JOIN ( SELECT 100000 AS code
UNION ALL SELECT 100002
) c
-- ant-join pattern to eliminate rows that have a match in MonthlySales
LEFT
JOIN `MonthlySales` s
ON s.`code` = c.code
AND s.`year` = y.yyyy
AND s.`month` = m.mm
WHERE s.`code` IS NULL
HAVING CONCAT(y.yyyy,'-',m.mm,'-01') < DATE_FORMAT(NOW(),'%Y-%m-01')
ORDER
BY c.code
, y.yyyy
, m.mm
That would give us the rows that are "missing". We could expand the expressions in the SELECT list to return literals
SELECT c.code AS `code`
, y.yyyy AS `year`
, m.mm AS `month`
, '0' AS `salesqty`
, '0.00' AS `salesvalue`
, '' AS `tvalue`
, '' AS `dvalue`
, '0.00' AS `cvalue`
FROM ...

Counting the occurence of record in mysql

I am trying to count a lactation which means i am counting the dates calved of an animal: when calvDate for the animalid changes 1 is added to the lactationID to keep count.
This are the five columns
#ID,LactationID,CalvDateLactationID, animalidLactationID,animalid, calvDate
'1', '1', '1 - 2018-08-08', '1 - T81', 'T81', '2018-08-08'
'2', '1', '1 - 2017-12-18', '1 - T66', 'T66', '2017-12-18'
'3', '2', '3 - 2017-12-28', '4 - T66', 'T66', '2017-12-28'
The query i am using to generate this output is
SELECT
dt.ID,
#row_num := IF(#aid <> dt.animalid, 1, #row_num + 1) as LactationID,
concat(#row_num := IF(#aid <> dt.animalid, 1, #row_num + 1),' - ',calvDate) AS CalvDateLactationID,
concat(#row_num := IF(#aid <> dt.animalid, 1, #row_num + 1),' - ',animalid) AS animalidLactationID,
#aid := dt.animalid AS animalid, dt.calvDate
FROM
(SELECT ID,animalid,calvDate FROM calvingdatecombined ORDER BY animalid, calvDate, ID) AS dt
CROSS JOIN (SELECT #row_num := 0, #aid := '') AS user_init_vars
where calvDate <> '' and calvDate <> '0000-00-00' ORDER BY dt.ID
My expected output is
#ID, LactationID,CalvDateLactationID, animalidLactationID,animalid, calvDate
'1', '1', '1 - 2018-08-08', '1 - T81', 'T81', '2018-08-08'
'2', '1', '1 - 2017-12-18', '1 - T66', 'T66', '2017-12-18'
'3', '2', '2 - 2017-12-28', '2 - T66', 'T66', '2017-12-28'
what can i improve in my query to help me generate my expected output.
My calvingdatecombined table has the following columns and sample data
# ID, animalid, calvDate
'1', 'T81', '2018-08-08'
'2', 'T66', '2017-12-18'
'3', 'T66', '2017-12-28'
Not an answer, just a suggestion, and too long for a comment:
A data set like this would lose none of the meaning, and would be considerably easier to read:
ID, animalid, calvDate
186, 81, '2018-08-08'
188, 66, '2017-12-18'
189, 66, '2017-12-28'
You don't need to increment #row_num value again for CalvDateLactationID and animalidLactationID.
Also, I have shifted the Where condition on calvDate to inner Select query, for further optimization. Use the following query instead:
SELECT
dt.ID,
#row_num := IF(#aid <> dt.animalid, 1, #row_num + 1) as LactationID,
concat(#row_num, ' - ', calvDate) AS CalvDateLactationID,
concat(#row_num, ' - ', animalid) AS animalidLactationID,
#aid := dt.animalid AS animalid,
dt.calvDate
FROM
(
SELECT ID,animalid,calvDate
FROM calvingdatecombined
WHERE calvDate > '0000-00-00'
ORDER BY animalid, calvDate, ID
) AS dt
CROSS JOIN (SELECT #row_num := 0,
#aid := '') AS user_init_vars
ORDER BY dt.ID

Counting most common word in SQL table with exclusions

I'm trying to count the most common words from a table full of text (strings) in a MySQL database (using MYSQL workbench). I got this code working from reading another post (written by Kickstart).
This code uses a separate table called integer with 10 columns from 0 to 9 for counting.
Table Schema for the main table. I'm mainly only interested in data the "Text" column.
'Id', 'int(11)', 'NO', 'PRI', '0', ''
'PostId', 'int(11)', 'YES', 'MUL', NULL, ''
'Score', 'int(11)', 'YES', 'MUL', NULL, ''
'Text', 'varchar(4000)', 'YES', '', NULL, ''
'CreationDate', 'varchar(25)', 'YES', '', NULL, ''
'UserId', 'int(11)', 'YES', 'MUL', NULL, ''
'UserDisplayName', 'varchar(255)', 'YES', '', NULL, ''
SQL query:
SELECT aWord, COUNT(*) AS WordOccuranceCount
FROM (SELECT SUBSTRING_INDEX(SUBSTRING_INDEX(concat(Text, ' '), ' ', aCnt), ' ', -1) AS aWord
FROM table
CROSS JOIN (
SELECT a.i+b.i*10+c.i*100 + 1 AS aCnt
FROM integers a, integers b, integers c) Sub1
WHERE (LENGTH(Body) + 1 - LENGTH(REPLACE(Text, ' ', ''))) >= aCnt) Sub2
WHERE Sub2.aWord != ''
GROUP BY aWord
ORDER BY WordOccuranceCount DESC
LIMIT 10
It lists out the top 10 words, but they are full of short words like a, the, you, me... etc.
How can I change it to skip certain words like those?
How can I make it so that say, only words 5 characters and up are counted?
Schema of integers table
'i', 'int(11)', 'NO', 'PRI', NULL, ''
Original post and code taken from this post. I am new and couldn't post anything on it so I had to ask here.
determining most used set of words php mysql
Thank you so much for your help!
You should be able to just add another condition to your WHERE clause:
SELECT aWord, COUNT(*) AS WordOccuranceCount
FROM (SELECT SUBSTRING_INDEX(SUBSTRING_INDEX(concat(Text, ' '), ' ', aCnt), ' ', -1) AS aWord
FROM table
CROSS JOIN (
SELECT a.i+b.i*10+c.i*100 + 1 AS aCnt
FROM integers a, integers b, integers c) Sub1
WHERE (LENGTH(Body) + 1 - LENGTH(REPLACE(Text, ' ', ''))) >= aCnt) Sub2
WHERE Sub2.aWord != '' AND
LENGTH(Sub2.aWord) >= 5
GROUP BY aWord
ORDER BY WordOccuranceCount DESC
LIMIT 10
Just checking to see if the length of aWord is at least 5 chars, and if so, include it in the result set. The LIMIT will be applied to the result set (post-filtering) and you should have what you need.

Recursive Fill Calculations with CTE or anything efficient

Please help me with ideas (preferably CTE) to solve this as efficient as possible.
So... In the table shown, the cells in column "Value" which are red are the known values
and the highlighted greens are values to be calculated with formulas shown next to them.
I am trying to see if this is possible with CTEs at all.
It's like the last known value and its respective interval; the next known value and the respective interval; and the interval for which the value is calculated for; all are used to find the value which then intern will be used the very same way for the next unknown value.
Here is a solution.
Hope it helps. :)
;with testdata(store,shipntrvl,value)
as
(
select 'abc', 1, 0.56
union all
select 'abc', 5, null
union all
select 'abc', 10, 0.63
union all
select 'abc', 15, null
union all
select 'abc', 20, null
union all
select 'abc', 25, null
union all
select 'abc', 30, 0.96
union all
select 'xyz', 1, 0.36
union all
select 'xyz', 5, 0.38
union all
select 'xyz', 10, null
union all
select 'xyz', 15, 0.46
union all
select 'xyz', 20, null
union all
select 'xyz', 25, null
union all
select 'xyz', 30, 0.91
)
,calc
as
(
select *
,ROW_NUMBER() OVER(partition by store order by shipntrvl) as row_no
from testdata
)
,extra
as
(
select *
,(select top 1 row_no
from calc c2
where c2.row_no < c1.row_no
and c1.value is null
and c2.value is not null
and c1.store = c2.store
order by c2.row_no desc) as prev_nr
,(select top 1 row_no
from calc c2
where c2.row_no > c1.row_no
and c1.value is null
and c2.value is not null
and c1.store = c2.store
order by c2.row_no asc) as next_nr
from calc c1
)
select c.store
,c.shipntrvl
,c.value
,isnull(c.value,
(cnext.value-cprev.value)/
(cnext.shipntrvl-cprev.shipntrvl)*
(c.shipntrvl-cprev.shipntrvl)+cprev.value
) as calculated_value
from calc c
join extra
on extra.row_no = c.row_no
and extra.store = c.store
join calc cnext
on cnext.row_no = case when c.value is null
then extra.next_nr
else c.row_no
end
and c.store = cnext.store
join calc cprev
on cprev.row_no = case when c.value is null
then extra.prev_nr
else c.row_no
end
and c.store = cprev.store
Here is what I came up with (storevalue is the beginning table in your example)
with knownvalues as (
select store, shipNtrvl,value
from storevalue where Value is not null
), valueranges as
(
select
k.store,
k.ShipNtrvl as lowrange,
MIN(s.ShipNtrvl) as highrange,
(select value from storevalue where store = k.store and ShipNtrvl = MIN(s.shipNtrvl))-
(select value from storevalue where store = k.store and ShipNtrvl = k.ShipNtrvl) as term1,
MIN(s.ShipNtrvl) - k.ShipNtrvl as term2,min(k.Value) as lowval
from knownvalues k
join storevalue s on s.Value is not null and s.store= k.store and s.ShipNtrvl > k.ShipNtrvl
group by k.store, k.shipntrvl
)
select s.store,s.ShipNtrvl,v.term1/v.term2*(s.ShipNtrvl-v.lowrange)+ v.lowval as value
from storevalue s join valueranges v on v.store = s.store and s.ShipNtrvl between v.lowrange and v.highrange
where s.Value is null
union
select * from storevalue where value is not null
Just change the select to an update to write the values into the table.

Multiple IF statements on MYSQL

I'm trying to Display somes values in my database result, I am using this code but I can not succeed:
SELECT
item_code,
IF(category_code = 'HERR1', 'NO', 1) OR (category_code = 'COLN5', 'NO', 2) AS category_code,
item_name,
item_quantity
FROM qa_items
EDIT :
I Want to display for example:
If category_code = 'HERR1'
Display = 1
else if category_code = 'COLN5'
Display = 2
End If
If anyone has any idea, would greatly appreciate it
I'd rather use CASE :
SELECT item_code,
CASE category_code
WHEN 'HERR1' THEN 1
WHEN 'COLN5' THEN 2
ELSE 'NO'
END as category_code, item_name, item_quantity
FROM qa_items
But IF will also work : IF(category_code='HERR1',1, IF(category_code='COLN5',2,'NO'))
You need to nest the if statements
SELECT item_code, IF(category_code = 'HERR1', 'NO', IF(category_code = 'COLN5', 1, 2)) AS category_code, item_name, item_quantity FROM qa_items
Then the first if will fail and the nested if will evaluate
Is this what you were after?
SELECT
item_code,
CASE category_code
WHEN 'HERR1' THEN 1
WHEN 'COLN5' THEN 2
ELSE 'NO'
END AS category_code,
item_name,
item_quantity
FROM qa_items
Try the following
SELECT item_code, CASE category_code WHEN 'HERR1' THEN 1 WHEN 'COLN5' THEN 0 ELSE 'NONE' END AS category_code, item_name, item_quantity FROM qa_items
You can try this.
Use IF in select query and update the table you want ;)
create table student(marks int,grade char);
insert into student values(200,null),(120,null),
(130,null);
UPDATE student a
INNER JOIN (select s.marks, IF(s.marks>=200,'A',IF(s.marks>=130,'B','P')) AS Grade from student s) b on a.marks= b.marks
SET a.Grade = b.Grade;
SELECT MOBILE,
CASE (SUBSTRING(mobile, LENGTH(MOBILE), 1)) WHEN ','
THEN SUBSTRING(mobile, 1, LENGTH(MOBILE) - 1)
ELSE SUBSTRING(mobile, 1, LENGTH(MOBILE))
END AS newmobile
FROM (SELECT CONCAT(IFNULL(`mob1`, ''), IF(`mob1` IS NULL, '', ','),
IFNULL(`mob2`, ''), IF(`mob2` IS NULL, '', ','),
IFNULL(`mob3`, ''), IF(`mob3` IS NULL, '', ','),
IFNULL(`mob4`, ''), IF(`mob4` IS NULL, '', ','),
IFNULL(`mob5`, ''), IF(`mob5` IS NULL, '', ','),
IFNULL(`mob6`, ''))
AS mobile
FROM `temp_consignordata`) AS T
SELECT item_code,
-- First if
IF(category_code = 'HERR1', 1,
-- second else IF
IF(category_code = 'COLN5', 2,
-- last else
'NO')
AS category_code,
item_name,
item_quantity
FROM qa_items;
Explanation
first if evalutes for value 'HERR1' and if found assigns 1
Second (else ) IF evalues for Value 'COLN5' and if found assigns 2
Last (else) default case assigns 'NO'
to category_code