Join another table and same table - mysql

need your help here
I've been working on this query for a day lol
I have 2 tables
table Users
and table payroll_variable
My goals here to Join table users with 2 or more table payroll_variable like this
This is what I do so far :
SELECT DISTINCT
u.id_user,
u.first_name,
u.last_name,
pv.year,
pv.base_salary,
CASE
WHEN pv.month = 'January' THEN pv.total_salary
ELSE NULL
END as 'January',
CASE
WHEN pv1.month = 'February' THEN pv1.total_salary
ELSE NULL
END as 'February'
FROM payroll_variable pv
INNER JOIN users u ON u.id_user = pv.id_user
INNER JOIN payroll_variable pv1 ON pv1.id_user = pv.id_user
WHERE pv.status != 'submit'
And this the wrong result from my query
The problem is that , i just want to show every user with only one loop like my goals, and don't mind my WHERE clause , it's just for flag-ing

First of all I'd changed name of two fields in my query i.e. month and year to payroll_month and payroll_year.
Here is the query that give you desired output
Select u.id_user, u.first_name, u.last_name, pv.payroll_year, pv.base_salary,
sum(IF(pv.payroll_month = 'January', pv.total_salary, 0)) as 'January',
sum(IF(pv.payroll_month = 'February', pv.total_salary, 0)) as 'February',
sum(IF(pv.payroll_month = 'March', pv.total_salary, 0)) as 'March',
sum(IF(pv.payroll_month = 'April', pv.total_salary, 0)) as 'April',
sum(IF(pv.payroll_month = 'May', pv.total_salary, 0)) as 'May',
sum(IF(pv.payroll_month = 'June', pv.total_salary, 0)) as 'June',
sum(IF(pv.payroll_month = 'July', pv.total_salary, 0)) as 'July',
sum(IF(pv.payroll_month = 'August', pv.total_salary, 0)) as 'August',
sum(IF(pv.payroll_month = 'September', pv.total_salary, 0)) as 'September',
sum(IF(pv.payroll_month = 'October', pv.total_salary, 0)) as 'October',
sum(IF(pv.payroll_month = 'November', pv.total_salary, 0)) as 'November',
sum(IF(pv.payroll_month = 'December', pv.total_salary, 0)) as 'December'
from users u join payroll_variable pv using(id_user)
group by u.id_user
And sample output as in image

The problem is that you do not ensure that you have a record when there was no payment in either january or february and also, you do not ensure that you have a single record for each logical pair of january and february for the same user. You will need to use LEFT JOIN to ensure you have a row for each user and you will need to ensure that you have a single row for each logical tuples. I do not understand the logic behind your where condition, so I might be wrong there. I assume that a single record is stored for january or february if there was any payment:
SELECT DISTINCT
u.id_user,
u.first_name,
u.last_name,
pv.year as year,
pv.base_salary,
CASE
WHEN pv.month = 'January' THEN pv.total_salary
ELSE 0
END as January,
CASE
WHEN pv.month = 'February' THEN pv.total_salary
ELSE 0
END as February,
FROM users u
LEFT JOIN payroll_variable jan ON u.id_user = jan.id_user
LEFT JOIN payroll_variable feb ON feb.id_user = u.id_user
WHERE jan.status <> 'submit' and feb.status <> 'submit' and (not (jan.id_user is null) or not (feb.id_user is null))
This code is not tested and it is possible that I misunderstood your intention.
EDIT
In order to group your results by users, you will need to use group by:
select id_user, first_name, last_name, max(t.year), max(base_salary), sum(January) as January, sum(February) as February
from (
SELECT
u.id_user,
u.first_name,
u.last_name,
min(pv.year) as year,
min(pv.base_salary) as base_salary,
sum(case when pv.payroll_month = 'January' then pv.total_salary else 0 end) as January,
sum(case when pv.payroll_month = 'February' then pv.total_salary else 0 end) as February
FROM users u
LEFT JOIN payroll_variable pv ON u.id_user = pv.id_user) t
group by t.id_user, t.first_name, t.last_name

Related

SQL to summarise solar power generated by quarter

I have a single MySQL table with 10 years of solar panel generation data (every 10 minutes, where generation is > zero).
I have no idea how to construct a query that will give me four values per year row, each value representing the summed total for each quarter in that year.
My source table (DTP) schema looks like this:
#, Field, Type, Null, Key, Default, Extra
1, 'PWR', 'decimal(5,3)', 'NO', '', NULL, ''
2, 'idDTP', 'int(11)', 'NO', 'PRI', NULL, 'auto_increment'
3, 'DT', 'datetime', 'NO', '', NULL, ''
One example I though might point me in the right direction looks like this (bear in mind this is partly modified from the source):
SELECT Year,SUM(Quarter1) AS Quarter1,SUM(Quarter2) AS Quarter2,SUM(Quarter3) AS Quarter3,SUM(Quarter4) AS Quarter4
FROM
(
SELECT YEAR(DT) AS 'Year' ,
Quarter1 = CASE(DATEPART(q, DTP.DT))
WHEN 1 THEN SUM(DTP.DT)
ELSE 0
END,
Quarter2 = CASE(DATEPART(q, DTP.DT))
WHEN 2 THEN SUM(DTP.DT)
ELSE 0
END,
Quarter3 = CASE(DATEPART(q, DTP.DT))
WHEN 3 THEN SUM(DTP.DT)
ELSE 0
END,
Quarter4 = CASE(DATEPART(q, DTP.DT))
WHEN 4 THEN SUM(DTP.DT)
ELSE 0
END
FROM DTP LEFT JOIN PWR ON DTP.DT = Customers.CustomerID
LEFT JOIN [Order Details] ON [Order Details].OrderID = Orders.OrderID
GROUP BY CompanyName, YEAR(OrderDate), DATEPART(q, OrderDate)
)C
GROUP BY CompanyName,Year
I started trying to convert it to my scenario but without understanding it, I've landed myself in a half finished (assuming this is even on the right lines) mess and I'm not sure how to fix it. Any well explained hints or links would be welcome - flames, less so. ;oP
If I've got the wrong forum, please politely point out the right one - couldn't see a better alternative in the Stack-Echange list.
Thanks
Small sample from first year of source data (Feb-Mar)
'160851', '2012-02-29 08:00:00', '0.030'
'160852', '2012-02-29 08:10:00', '0.066'
'160853', '2012-02-29 08:20:00', '0.072'
'160854', '2012-02-29 08:30:00', '0.060'
'160855', '2012-02-29 08:40:00', '0.090'
'160856', '2012-02-29 08:50:00', '0.102'
'160857', '2012-02-29 09:00:00', '0.084'
'160858', '2012-02-29 09:10:00', '0.132'
'160859', '2012-02-29 09:20:00', '0.144'
'160860', '2012-02-29 09:30:00', '0.138'
'160861', '2012-02-29 09:40:00', '0.150'
'160862', '2012-02-29 09:50:00', '0.174'
'160863', '2012-02-29 10:00:00', '0.174'
'160864', '2012-02-29 10:10:00', '0.162'
I can't enter a years worth of data, as it unsurprisingly exceeds the allowed count but it proceeds in a similar vein.
There's no meaningful output because I got out of my depth well before I'd approached anything like viable code.
Define a new year-quarter column, named yq.
Fill it with values like "2021-q4" and "2022-q1".
Now your problem is simple.
Just compute SUM( ... )
with GROUP BY yq.
You are missing some tables, but i kept them in the subquery.
the subwquery, would show the numbers for all quaters , for every company and year.
The outer query, would, sum them up into years without companies..
As you need more columns you can add them from comapanies orders or order details and add them in the inner SELECT and then add them also in the outer, to group them for them examle by orders or such
SELECT Year,SUM(Quarter1) AS Quarter1,SUM(Quarter2) AS Quarter2,SUM(Quarter3) AS Quarter3,SUM(Quarter4) AS Quarter4
FROM
(
SELECT CompanyName,YEAR(DT) AS 'Year' ,
SUM(CASE WHEN DATEPART(q, DTP.DT) = 1 THEN DTP.PWR ELSe 0 END) Quarter1,
SUM(CASE WHEN DATEPART(q, DTP.DT) = 2 THEN DTP.PWR ELSe 0 END) Quarter2,
SUM(CASE WHEN DATEPART(q, DTP.DT) = 3 THEN DTP.PWR ELSe 0 END) Quarter3,
SUM(CASE WHEN DATEPART(q, DTP.DT) = 4 THEN DTP.PWR ELSe 0 END) Quarter4
FROM DTP LEFT JOIN PWR ON DTP.DT = Customers.CustomerID
LEFT JOIN [Order Details] ON [Order Details].OrderID = Orders.OrderID
GROUP BY CompanyName, YEAR(OrderDate)
) C
GROUP BY Year
Without the customer and orders
SELECT CompanyName,YEAR(DT) AS 'Year' ,
SUM(CASE WHEN DATEPART(q, DTP.DT) = 1 THEN DTP.PWR ELSe 0 END) Quarter1,
SUM(CASE WHEN DATEPART(q, DTP.DT) = 2 THEN DTP.PWR ELSe 0 END) Quarter2,
SUM(CASE WHEN DATEPART(q, DTP.DT) = 3 THEN DTP.PWR ELSe 0 END) Quarter3,
SUM(CASE WHEN DATEPART(q, DTP.DT) = 4 THEN DTP.PWR ELSe 0 END) Quarter4
FROM DTP
GROUP BY YEAR(DT)

MySQL - Get users who have NO orders in current month but don have in previous, in one request

I need to get users count, grouped by user type (A,B,C) and every month (that exist in db) in current year - only for users who don't have paid orders (with total > 0) in every month (every row returned by SQL), but have orders (with total > 0) in any previous months (in any year, not just current). In other words this is inactive users, who placed some paid order before, but don't placed any new orders in current SQL request row month returned.
What I expect to get in results (values are just examples):
label user_type data
Nov B 2
Nov A 1
Nov C 3
Dec C 1
.... other months
This means that in December there are 5 users with user type A and 3 users with user type B and 0 users with user type C, who DON'T placed orders in December 2021, but placed orders sometime before December in any year.
Sample DB (two tables - users and orders) with SQL that show number users, by every user type, in every month, who placed orders in this month. Instead of just this simple results, I need to get users counts that DON'T placed orders in this month, but placed paid orders somewhere before.
https://dbfiddle.uk/?rdbms=mysql_5.6&fiddle=4c4fadf67bcdc7cc3443f46c387173df
I need SQL that will work with MySQL 5.7
Try this query to generate counts for all months x user type
SELECT
DATE_FORMAT(DATE(CONCAT_WS('-', YEAR(CURDATE()), months.mm, '01')), "%b") as label,
users.user_type,
SUM(
EXISTS (
SELECT 1
FROM orders
WHERE orders.user_id = users.userid
AND orders.`date` < DATE(CONCAT_WS('-', YEAR(CURDATE()), months.mm, '01'))
) AND NOT EXISTS (
SELECT 1
FROM orders
WHERE orders.user_id = users.userid
AND orders.`date` BETWEEN DATE(CONCAT_WS('-', YEAR(CURDATE()), months.mm, '01')) AND LAST_DAY(DATE(CONCAT_WS('-', YEAR(CURDATE()), months.mm, '01')))
)
) counts
FROM (
SELECT '01' mm
UNION SELECT '02' UNION SELECT '03' UNION SELECT '04' UNION SELECT '05'
UNION SELECT '06' UNION SELECT '07' UNION SELECT '08' UNION SELECT '09'
UNION SELECT '10' UNION SELECT '11' UNION SELECT '12'
) months
CROSS JOIN users
GROUP BY months.mm, users.user_type
demo
Test this query if it fits your needs
SELECT DATE_FORMAT(o.date, "%b") as label,
UPPER(u.user_type) as user_type,
COUNT(distinct o.user_id) as data FROM orders o
JOIN users u ON o.user_id = u.userid
WHERE DATE_FORMAT(o.date, "%Y") = "2021"
AND o.user_id NOT IN
(SELECT DISTINCT o1.user_id FROM orders o1 WHERE DATE_FORMAT(o1.date, "%b") = DATE_FORMAT(now(), "%b") AND YEAR(o1.date) = YEAR(now()) )
AND o.user_id IN
(SELECT DISTINCT o1.user_id FROM orders o1 WHERE (DATE_FORMAT(o1.date, "%c") < DATE_FORMAT(now(), "%c") OR YEAR(o1.date) < YEAR(now())))
GROUP BY DATE_FORMAT(o.date, "%Y %b"),
u.user_type HAVING SUM(o.total) > 0 ORDER BY o.date ASC
EDIT
The query below returns every month of the year
SELECT months.MONTH as label,
ifnull(UPPER(u.user_type), '-') as user_type,
COUNT(distinct o.user_id) as data
FROM (
SELECT 1 AS MONTH
UNION SELECT 2 AS MONTH
UNION SELECT 3 AS MONTH
UNION SELECT 4 AS MONTH
UNION SELECT 5 AS MONTH
UNION SELECT 6 AS MONTH
UNION SELECT 7 AS MONTH
UNION SELECT 8 AS MONTH
UNION SELECT 9 AS MONTH
UNION SELECT 10 AS MONTH
UNION SELECT 11 AS MONTH
UNION SELECT 12 AS MONTH
) as months
LEFT JOIN orders o
ON DATE_FORMAT(o.date, "%c") = months.MONTH
LEFT JOIN users u ON o.user_id = u.userid
WHERE (DATE_FORMAT(o.date, "%Y") = "2021" OR o.date IS NULL)
AND (
(
NOT EXISTS
(SELECT DISTINCT o1.user_id
FROM orders o1
WHERE
DATE_FORMAT(o1.date, "%b") = DATE_FORMAT(now(), "%b")
AND YEAR(o1.date) = YEAR(now())
AND o1.user_id = o.user_id
)
AND EXISTS
(SELECT DISTINCT o1.user_id
FROM orders o1
WHERE
(DATE_FORMAT(o1.date, "%c") < DATE_FORMAT(now(), "%c") OR YEAR(o1.date) < YEAR(now())) AND o1.user_id = o.user_id
)
)
OR o.user_id IS null OR u.userid IS NULL
)
GROUP BY months.MONTH, u.user_type ORDER BY months.MONTH ASC
This uses a similar approach to VeteranSlayer but it starts with the cross join between months and users followed by the left join to orders. It also uses ranges for the date comparisons instead of the functions. It may perform really badly but it should give the correct result -
SELECT
months.month AS `label`,
u.user_type,
COUNT(u.userid) AS `data`
FROM (
SELECT 'Jan' `month`, '2021-01-01' month_start, '2021-01-31' month_end UNION ALL
SELECT 'Feb', '2021-02-01', '2021-02-28' UNION ALL
SELECT 'Mar', '2021-03-01', '2021-03-31' UNION ALL
SELECT 'Apr', '2021-04-01', '2021-04-30' UNION ALL
SELECT 'May', '2021-05-01', '2021-05-31' UNION ALL
SELECT 'Jun', '2021-06-01', '2021-06-30' UNION ALL
SELECT 'Jul', '2021-07-01', '2021-07-31' UNION ALL
SELECT 'Aug', '2021-08-01', '2021-08-31' UNION ALL
SELECT 'Sep', '2021-09-01', '2021-09-30' UNION ALL
SELECT 'Oct', '2021-10-01', '2021-10-31' UNION ALL
SELECT 'Nov', '2021-11-01', '2021-11-30' UNION ALL
SELECT 'Dec', '2021-12-01', '2021-12-31'
) months
INNER JOIN users u
LEFT JOIN orders o
ON o.date BETWEEN months.month_start AND months.month_end
AND o.user_id = u.userid
WHERE o.user_id IS NULL
AND EXISTS (
SELECT DISTINCT o1.user_id
FROM orders o1
WHERE o1.date < months.month_start
AND o1.user_id = u.userid
)
GROUP BY months.month, u.user_type
ORDER BY months.month_start ASC, u.user_type ASC;
EDIT
The performance of these queries varies dramatically based on the scale of the dataset, the distribution of data and the indices. I have done some tests with many different index variations and the following test datasets. Note the random data created in the two tables can lead to wildly different performance. The dummy table referenced in the SELECTs of the INSERTs is just a random table with 1M rows.
CREATE TABLE `users` (
`id` int unsigned NOT NULL AUTO_INCREMENT PRIMARY KEY,
`user_type` char(1) NOT NULL,
KEY `IDX_user_type` (`user_type`)
);
INSERT INTO users (user_type)
SELECT
CASE (FLOOR(RAND() * 3) + 1) WHEN 1 THEN 'A' WHEN 2 THEN 'B' ELSE 'C' END AS `user_type`
FROM dummy
LIMIT 1000;
CREATE TABLE orders (
`id` int UNSIGNED NOT NULL AUTO_INCREMENT PRIMARY KEY,
`user_id` int,
`date` DATE,
`total` DECIMAL(6,2),
KEY `IDX_user_id_date` (`user_id`,`date`)
);
INSERT INTO orders (user_id, date, total)
SELECT
(FLOOR(RAND() * 1000) + 1) AS `user_id`,
('2020-01-01' + INTERVAL FLOOR(RAND() * 685) + 1 DAY) AS `date`,
( (FLOOR(RAND() * 10) + 1) * 5) AS `total`
FROM dummy
LIMIT 100000;
The most significant performance difference across the queries came from adding -
KEY `IDX_user_id_date` (`user_id`,`date`)
and adding the user_type index gave a small but consistent improvement -
KEY `IDX_user_type` (`user_type`)
ProGu's query executed consistently with an average time of 1.466 sec. And my query was similarly consistent at 0.922 sec. Your mileage will vary!
I haven't included time's for VeteranSlayer's query as it returned radically different results.
EDIT 2
Repopulated the two tables with 50k users and 1M orders
TRUNCATE TABLE orders;
TRUNCATE TABLE users;
INSERT INTO users (user_type)
SELECT
CASE (FLOOR(RAND() * 3) + 1) WHEN 1 THEN 'A' WHEN 2 THEN 'B' ELSE 'C' END AS `user_type`
FROM (SELECT 1 FROM dummy LIMIT 50000) t;
INSERT INTO orders (user_id, date, total)
SELECT
(FLOOR(RAND() * 50000) + 1),
TIMESTAMPADD(SECOND, FLOOR(RAND() * TIMESTAMPDIFF(SECOND, '2016-01-01', '2021-12-13')), '2016-01-01'),
((FLOOR(RAND() * 50) + 1) * 5)
FROM (SELECT 1 FROM dummy LIMIT 1000000) t
ORDER BY date;
The resulting distribution of orders, by time and user_id, is quite even which is unlikely to be realistic so this test dataset grossly exacerbates any performance issues, I think.
I was surprised that by using my months table, ProGu's query was significantly faster, dropping from 21.062sec to 9.703sec, and using one less temporary table (two instead of three).
SELECT
months.month as label,
users.user_type,
SUM(
EXISTS (
SELECT 1
FROM orders
WHERE orders.user_id = users.id
AND orders.`date` < months.month_start
) AND NOT EXISTS (
SELECT 1
FROM orders
WHERE orders.user_id = users.id
AND orders.`date` BETWEEN months.month_start AND months.month_end
)
) counts
FROM (
SELECT 'Jan' `month`, '2021-01-01' month_start, '2021-01-31' month_end UNION ALL
SELECT 'Feb', '2021-02-01', '2021-02-28' UNION ALL
SELECT 'Mar', '2021-03-01', '2021-03-31' UNION ALL
SELECT 'Apr', '2021-04-01', '2021-04-30' UNION ALL
SELECT 'May', '2021-05-01', '2021-05-31' UNION ALL
SELECT 'Jun', '2021-06-01', '2021-06-30' UNION ALL
SELECT 'Jul', '2021-07-01', '2021-07-31' UNION ALL
SELECT 'Aug', '2021-08-01', '2021-08-31' UNION ALL
SELECT 'Sep', '2021-09-01', '2021-09-30' UNION ALL
SELECT 'Oct', '2021-10-01', '2021-10-31' UNION ALL
SELECT 'Nov', '2021-11-01', '2021-11-30' UNION ALL
SELECT 'Dec', '2021-12-01', '2021-12-31'
) months
CROSS JOIN users
GROUP BY months.month, users.user_type
ORDER BY months.month_start ASC, users.user_type ASC
My query above can be significantly improved by pre grouping the orders data for the current year (your mileage will vary but worth considering) -
SELECT
months.month AS `label`,
u.user_type,
COUNT(u.id) AS `data`
FROM (
SELECT 'Jan' `month`, '2021-01-01' month_start, '2021-01-31' month_end UNION ALL
SELECT 'Feb', '2021-02-01', '2021-02-28' UNION ALL
SELECT 'Mar', '2021-03-01', '2021-03-31' UNION ALL
SELECT 'Apr', '2021-04-01', '2021-04-30' UNION ALL
SELECT 'May', '2021-05-01', '2021-05-31' UNION ALL
SELECT 'Jun', '2021-06-01', '2021-06-30' UNION ALL
SELECT 'Jul', '2021-07-01', '2021-07-31' UNION ALL
SELECT 'Aug', '2021-08-01', '2021-08-31' UNION ALL
SELECT 'Sep', '2021-09-01', '2021-09-30' UNION ALL
SELECT 'Oct', '2021-10-01', '2021-10-31' UNION ALL
SELECT 'Nov', '2021-11-01', '2021-11-30' UNION ALL
SELECT 'Dec', '2021-12-01', '2021-12-31'
) months
INNER JOIN users u
LEFT JOIN (
SELECT `user_id`, DATE_FORMAT(`date`, '%Y-%m-01') AS `m`
FROM `orders`
WHERE `date` >= '2021-01-01'
GROUP BY `user_id`, `m`
) o
ON o.m = months.month_start
AND o.user_id = u.id
WHERE o.user_id IS NULL
AND EXISTS (
SELECT 1
FROM orders o1
WHERE o1.date < months.month_start
AND o1.user_id = u.id
)
GROUP BY months.month, u.user_type
ORDER BY months.month_start ASC, u.user_type ASC
Execution time dropped from 12.422sec to 6.497sec
And the final test I tried was de-normalising by adding first_order_date to the users table -
ALTER TABLE `users` ADD COLUMN `first_order_date` DATE NULL AFTER `user_type`;
UPDATE users u
INNER JOIN (SELECT o.user_id, MIN(date) AS `first_o`, MAX(date) AS `last_o` FROM orders o GROUP BY o.user_id) t ON u.id = t.user_id
SET `u`.`first_order_date` = `t`.`first_o`, `u`.`last_order_date` = `t`.`last_o`;
I then modified my query to use this instead of the EXISTS sub-query -
SELECT
`months`.`month` AS `label`,
`u`.`user_type`,
COUNT(`u`.`id`) AS `data`
FROM (
SELECT 'Jan' `month`, '2021-01-01' month_start, '2021-01-31' month_end UNION ALL
SELECT 'Feb', '2021-02-01', '2021-02-28' UNION ALL
SELECT 'Mar', '2021-03-01', '2021-03-31' UNION ALL
SELECT 'Apr', '2021-04-01', '2021-04-30' UNION ALL
SELECT 'May', '2021-05-01', '2021-05-31' UNION ALL
SELECT 'Jun', '2021-06-01', '2021-06-30' UNION ALL
SELECT 'Jul', '2021-07-01', '2021-07-31' UNION ALL
SELECT 'Aug', '2021-08-01', '2021-08-31' UNION ALL
SELECT 'Sep', '2021-09-01', '2021-09-30' UNION ALL
SELECT 'Oct', '2021-10-01', '2021-10-31' UNION ALL
SELECT 'Nov', '2021-11-01', '2021-11-30' UNION ALL
SELECT 'Dec', '2021-12-01', '2021-12-31'
) `months`
INNER JOIN `users` `u`
LEFT JOIN (
SELECT `user_id`, DATE_FORMAT(`date`, '%Y-%m-01') AS `m`
FROM `orders`
WHERE `date` >= '2021-01-01'
GROUP BY `user_id`, `m`
) o
ON `o`.`m` = `months`.`month_start`
AND `o`.`user_id` = `u`.`id`
WHERE `o`.`user_id` IS NULL
AND `u`.`first_order_date` < `months`.`month_start`
GROUP BY `months`.`month`, `u`.`user_type`
ORDER BY `months`.`month_start` ASC, `u`.`user_type` ASC;
This returns the same result in 1.447sec. Obviously, de-normalising like this should be avoided but I included it here as it shows the performance benefit for this one scenario.

An expression of non boolean type specified in a context where condition is expected near )

The Query mentioned below works fine if I remove the Where clause.. Any Suggestions please??
SELECT dbo.MPProjektBuchungserfassung.BuchungsErfassungID, dbo.MPProjektBuchungserfassung.QuellReferenz, dbo.MPProjektstamm.Projektnummer,
dbo.MPProjektstamm.Projekttyp, dbo.MPProjektstamm.Matchcode AS ProjektMatchcode, dbo.MPProjektPhasen.PhasenID,
dbo.MPProjektPhasen.Phase AS PhasenMatchcode, MPProjektleiterstamm.Personalnummer AS ProjektLeiternummer,
MPProjektleiterstamm.Matchcode AS ProjektLeiterMatchcode, dbo.MPVKVertraege.VertragsID, dbo.MPVKVertraege.Vertragsnummer,
dbo.MPVKVertraege.Bezeichnung, dbo.MPVKVertraege.KHKMandant, dbo.MPVKVertraege.Referenznummer,
dbo.MPVKVertraege.Referenzzeichen, dbo.MPVKVertraegePositionen.Vertragsposition, dbo.MPVKVertraegePositionen.Matchcode AS PositionsMatchcode,
dbo.MPHotlineAnfrage.AnfrageID, dbo.MPHotlineAnfrage.Kurzbeschreibung, dbo.MPHotlineAnfrage.AnfrageText, dbo.MPHotlineAnfrage.CardCode,
dbo.MPHotlineAnfrage.AdressMatchcode, dbo.MPHotlineAnfrage.Referenz, dbo.MPPersonenstamm.Personalnummer, IsNull(dbo.MPVKVertraegePositionen.USER_Ratio, 1) AS 'Ratio',
dbo.MPPersonenstamm.Matchcode AS PersonalMatchcode, dbo.MPVKVertraege.Projektnummer,
case dbo.MPPerioden.BezeichnungPeriode when 'Januar' then '01.January' when 'Februar' then '02.February' when 'März' then '03.March'
when 'April' then '04.April' when 'Mai' then '05.May' when 'Juni' then '06.June'
when 'Juli' then '07.July' when 'August' then '08.August' when 'September' then '09.September'
when 'Oktober' then '10.October' when 'November' then '11.November' when 'Dezember' then '12.December'
End as 'Month',dbo.MPPerioden.BezeichnungJahr,
dbo.MPProjektBuchungskopf.Periode, dbo.MPProjektBuchungserfassung.LeistungsTag, dbo.MPProjektBuchungserfassung.Menge,
dbo.MPProjektBuchungserfassung.MengeFAK as 'Horas', dbo.MPProjektBuchungserfassung.Aktivitaet, dbo.MPProjektBuchungserfassung.Memo,
dbo.MPProjektBuchungserfassung.Freigabe, MPProjektLeistungserfassung.FakturaID,
CASE dbo.MPHotlineAnfrage.Status WHEN '1' THEN 'New' WHEN '2' THEN 'In Process' WHEN '3' THEN 'Customer Action' WHEN '4' THEN '3rd Party Investigation' WHEN
'5' THEN 'Closed' END AS 'Status',dbo.MPPerioden.Periode as 'Ind Periode',
T2.ContractNo,T2.Contract_Desc, ISNULL(T2.Q1, 0) AS 'Q1',
ISNULL(T2.January, 0) AS 'January', ISNULL(T2.February, 0) AS 'February', ISNULL(T2.March, 0) AS 'March',
ISNULL(T2.Invoiced_Q1, 0) AS 'Invoiced_Q1', ISNULL(T2.Q2, 0) AS 'Q2', ISNULL(T2.April, 0) AS 'April',
ISNULL(T2.May,0) AS 'May', ISNULL(T2.June, 0) AS 'June', ISNULL(T2.Invoiced_Q2, 0) AS 'Invoiced_Q2',
ISNULL(T2.Q3, 0) AS 'Q3', ISNULL(T2.July, 0) AS 'July', ISNULL(T2.August, 0) AS 'August',
ISNULL(T2.Invoiced_Q3, 0) AS 'Invoiced_Q3', ISNULL(T2.Q4, 0) AS 'Q4', ISNULL(T2.September, 0) AS 'September',
ISNULL(T2.October, 0) AS 'October',ISNULL(T2.November, 0) AS 'November', ISNULL(T2.December, 0) AS 'December',
ISNULL(T2.Invoiced_Q4, 0) AS 'Invoiced_Q4',T2.Remaining_Year, T2.Comments, T2.Q1_Comments, T2.January_Comments,
T2.February_Comments, T2.March_Comments, T2.Q2_Comments, T2.April_Comments, T2.May_Comments, T2.June_Comments,
T2.Q3_Comments, T2.July_Comments, T2.August_Comments,T2.September_Comments, T2.Q4_Comments, T2.October_Comments,
T2.November_Comments, T2.December_Comments
, (SELECT Count(MPProjektLeistungserfassung.BuchungserfassungID) AS LCount
FROM MPProjektLeistungserfassung
WHERE MPProjektLeistungserfassung.BuchungserfassungID = MPProjektBuchungserfassung.BuchungsErfassungID
AND MPProjektLeistungserfassung.Position > 0) AS Zuschlag
FROM dbo.MPProjektBuchungserfassung INNER JOIN
dbo.MPProjektBuchungskopf ON dbo.MPProjektBuchungserfassung.BuchungsKopf = dbo.MPProjektBuchungskopf.BuchungsKopfID INNER JOIN
dbo.MPProjektLeistungserfassung AS MPProjektLeistungserfassung ON
dbo.MPProjektBuchungserfassung.BuchungsErfassungID = MPProjektLeistungserfassung.BuchungserfassungID AND
MPProjektLeistungserfassung.Position = 0 INNER JOIN
dbo.MPProjektstamm ON dbo.MPProjektBuchungserfassung.Projektnummer = dbo.MPProjektstamm.Projektnummer INNER JOIN
dbo.vwMPProjektleiter ON dbo.vwMPProjektleiter.Projektnummer = dbo.MPProjektBuchungserfassung.Projektnummer LEFT OUTER JOIN
dbo.MPProjektPhasen ON dbo.MPProjektBuchungserfassung.PhasenID = dbo.MPProjektPhasen.PhasenID INNER JOIN
dbo.MPVKVertraege ON MPProjektLeistungserfassung.VertragsID = dbo.MPVKVertraege.VertragsID INNER JOIN
dbo.MPVKVertraegePositionen ON MPProjektLeistungserfassung.Vertragsposition = dbo.MPVKVertraegePositionen.Vertragsposition left outer JOIN
dbo.MPHotlineAnfrage ON dbo.MPHotlineAnfrage.AnfrageID = dbo.MPProjektBuchungserfassung.QuellReferenz INNER JOIN
dbo.MPPersonenstamm AS MPProjektleiterstamm ON dbo.vwMPProjektleiter.Personalnummer = MPProjektleiterstamm.Personalnummer INNER JOIN
dbo.MPPersonenstamm ON dbo.MPProjektBuchungskopf.Personalnummer = dbo.MPPersonenstamm.Personalnummer INNER JOIN
dbo.MPPerioden ON dbo.MPProjektBuchungskopf.Periode = dbo.MPPerioden.PeriodeJahr
LEFT OUTER JOIN dbo.MP_USER_Retainers AS T2 ON dbo.MPVKVertraege.VertragsID= T2.ContractNo
where DATEADD(MONTH,DATEDIFF(MONTH,-1,GETDATE())-1, -1)
order by dbo.MPProjektBuchungskopf.Periode
You have missed to check the condition in where clause
WHERE Dateadd(MONTH,Datediff(MONTH,-1,Getdate())-1,-1) = somedatecolumn -- Missed
where DATEADD(MONTH,DATEDIFF(MONTH,-1,GETDATE())-1, -1)
really means nothing. You will need to evaluate or equate with another value. For example,
where dbo.mytable.mycolumn = DATEADD(MONTH,DATEDIFF(MONTH,-1,GETDATE())-1, -1)

Msql Crosstab month rows as columns?

Good morning, I am unsuccessfully trying to create a report using mysql/php, I would like to have rows as columns similar to Access Crosstab or Excel Pivot. I have sales staff & monthly sales which I would like to display across the page with month as column headers.
My query below outputs the data but every salesperson has a row for every month which doesnt read very well,
Jan | Feb | Mar | April
Sales1
Sales2
Sales3
Sales4
Select
tblcontacts.ContactFullName,
Count(tblcases.CaseID) As cases,
MonthName(tblcases.CaseDate) As Monthly
From
tblcases Inner Join
tblcontacts On tblcases.ContactAssignedTo =
tblcontacts.ContactID
Group By
tblcontacts.ContactFullName,
MonthName(tblcases.CaseDate)
with rollup
Ant advice or pointers appreciated, I have researched but most of it went right over my head!
Kind regards
What you can do is simply group by each contact and use conditional aggregation to count rows based on each month:
SELECT
a.ContactFullName,
SUM(MONTH(b.CaseDate) = 1) AS Jan,
SUM(MONTH(b.CaseDate) = 2) AS Feb,
SUM(MONTH(b.CaseDate) = 3) AS Mar,
SUM(MONTH(b.CaseDate) = 4) AS Apr,
SUM(MONTH(b.CaseDate) = 5) AS May,
SUM(MONTH(b.CaseDate) = 6) AS Jun,
SUM(MONTH(b.CaseDate) = 7) AS Jul,
SUM(MONTH(b.CaseDate) = 8) AS Aug,
SUM(MONTH(b.CaseDate) = 9) AS Sep,
SUM(MONTH(b.CaseDate) = 10) AS Oct,
SUM(MONTH(b.CaseDate) = 11) AS Nov,
SUM(MONTH(b.CaseDate) = 12) AS Dec
FROM
tblcontacts a
INNER JOIN
tblcases b ON a.ContactID = b.ContactAssignedTo
GROUP BY
a.ContactFullName
Edit: As per your comments to this answer: to get a price sum of each month, you can do something like:
SELECT
a.ContactFullName,
SUM(IF(MONTH(b.CaseDate) = 1, b.price, 0)) AS Jan,
SUM(IF(MONTH(b.CaseDate) = 2, b.price, 0)) AS Feb,
SUM(IF(MONTH(b.CaseDate) = 3, b.price, 0)) AS Mar,
SUM(IF(MONTH(b.CaseDate) = 4, b.price, 0)) AS Apr,
SUM(IF(MONTH(b.CaseDate) = 5, b.price, 0)) AS May,
SUM(IF(MONTH(b.CaseDate) = 6, b.price, 0)) AS Jun,
SUM(IF(MONTH(b.CaseDate) = 7, b.price, 0)) AS Jul,
SUM(IF(MONTH(b.CaseDate) = 8, b.price, 0)) AS Aug,
SUM(IF(MONTH(b.CaseDate) = 9, b.price, 0)) AS Sep,
SUM(IF(MONTH(b.CaseDate) = 10, b.price, 0)) AS Oct,
SUM(IF(MONTH(b.CaseDate) = 11, b.price, 0)) AS Nov,
SUM(IF(MONTH(b.CaseDate) = 12, b.price, 0)) AS Dec
FROM
tblcontacts a
INNER JOIN
tblcases b ON a.ContactID = b.ContactAssignedTo
GROUP BY
a.ContactFullName
Basically, for each row, if the casedate is in a particular month, pass the value of the price column to the SUM aggregation, otherwise, just pass it 0.
Select
tblcontacts.ContactFullName,
sum(case when MonthName(tblcases.CaseDate)='January' then 1 else 0 end) as January,
sum(case when MonthName(tblcases.CaseDate)='February' then 1 else 0 end) as February,
.
.
sum(case when MonthName(tblcases.CaseDate)='December' then 1 else 0 end) as December,
From
tblcases Inner Join
tblcontacts On tblcases.ContactAssignedTo =
tblcontacts.ContactID
Group By
tblcontacts.ContactFullName

Rewriting MySQL query with subqueries to joins

I have written a fairly complex SQL query to get some statistics about animals from an animal sampling database. This query includes a number of subqueries and I would now like to see if it is possible to rewrite this query in any way to use joins instead of subqueries. I have a dim idea that this might reduce query time. (it's now about 23s on a mac mini).
Here's the query:
SELECT COUNT(DISTINCT a.AnimalID), TO_DAYS(a.VisitDate) AS day,
DATE_FORMAT(a.VisitDate, '%b %d %Y'), a.origin,
(
SELECT COUNT(DISTINCT a.AnimalID)
FROM samples AS a
JOIN
custom_animals AS b
ON a.AnimalID = b.animal_id
WHERE
b.organism = 2
AND
TO_DAYS(a.VisitDate) = day
) AS Goats,
(
SELECT COUNT(DISTINCT a.AnimalID)
FROM samples AS a
JOIN custom_animals AS b
ON a.AnimalID = b.animal_id
WHERE
b.organism = 2
AND
b.sex = 'Female'
AND
TO_DAYS(a.VisitDate) = day
) AS GF,
(
SELECT COUNT(DISTINCT a.AnimalID)
FROM samples AS a
JOIN custom_animals AS b
ON a.AnimalID = b.animal_id
WHERE
b.organism = 3
AND
b.sex = 'Female'
AND
TO_DAYS(a.VisitDate) = day
) AS SF
FROM
samples AS a
JOIN custom_animals AS b
ON a.AnimalID = b.animal_id
WHERE
project = 5
AND
AnimalID LIKE 'AVD%'
GROUP BY
TO_DAYS(a.VisitDate);
Thanks to ksogor my query is now way faster at;
SELECT DATE_FORMAT(s.VisitDate, '%b %d %Y') AS date,
s.origin,
SUM(IF(project = 5 AND s.AnimalID LIKE 'AVD%', 1, 0)) AS sampled_animals,
SUM(IF(ca.organism = 2, 1, 0)) AS sampled_goats,
SUM(IF(ca.organism = 2 AND ca.sex = 'Female', 1, 0)) AS female_goats,
SUM(IF(ca.organism = 3 AND ca.sex = 'Female', 1, 0)) AS female_sheep
FROM samples s JOIN custom_animals ca ON s.AnimalID = ca.animal_id
GROUP BY date;
I would still need to make this query select distinct s.AnimalID though as right now it counts the samples we have taken from these animals instead of the animals themselves. Anyone got any idea?
After some more help from ksogor I now have a great query:
SELECT DATE_FORMAT(s.VisitDate, '%b %d %Y') AS date,
s.origin,
SUM(IF(project = 5 AND s.AnimalID LIKE 'AVD%', 1, 0)) AS sampled_animals,
SUM(IF(ca.organism = 2, 1, 0)) AS sampled_goats,
SUM(IF(ca.organism = 2 AND ca.sex = 'Female', 1, 0)) AS female_goats,
SUM(IF(ca.organism = 3 AND ca.sex = 'Female', 1, 0)) AS female_sheep
FROM (
SELECT DISTINCT AnimalID AS AnimalID,
VisitDate,
origin,
project
FROM samples
) s
JOIN custom_animals ca ON s.AnimalID = ca.animal_id
GROUP BY date;
You can just use if or case statements, like this:
SELECT SUM(if(project = 5 AND AnimealID LIKE 'AVD%', 1, 0)) AS countbyproj,
TO_DAYS(s.VisitDate) AS day,
DATE_FORMAT(s.VisitDate, '%b %d %Y') AS date,
s.origin,
SUM(if(ca.organism = 2, 1, 0)) AS countGoats,
SUM(if(ca.organism = 2 AND ca.sex = 'Female', 1, 0)) AS countGF,
SUM(if(ca.organism = 3 AND ca.sex = 'Female', 1, 0)) AS countSF
FROM samples s JOIN custom_animals ca ON s.AnimalID = ca.animal_id
GROUP BY TO_DAYS(a.VisitDate);
I can't check query, I don't know what result you're expected and which tables/relations you have, so this is only example with idea.
If you need count unque AnimealID's for each day:
SELECT SUM(byproj) AS countbyproj,
day,
date,
origin,
SUM(Goats) AS countGoats,
SUM(GF) AS countGF,
SUM(SF) AS countSF
FROM (
SELECT s.AnimealID,
if(project = 5 AND AnimealID LIKE 'AVD%', 1, 0) AS byproj,
TO_DAYS(s.VisitDate) AS day,
DATE_FORMAT(s.VisitDate, '%b %d %Y') AS date,
s.origin,
if(ca.organism = 2, 1, 0)) AS Goats,
if(ca.organism = 2 AND ca.sex = 'Female', 1, 0) AS GF,
if(ca.organism = 3 AND ca.sex = 'Female', 1, 0) AS SF
FROM samples s JOIN custom_animals ca ON s.AnimalID = ca.animal_id
) dataset
GROUP BY dataset.day, dataset.AnimealID;