This is my table structure with garbage data inserted. What I am wanting to do is for each sales man (Frank and Joe) have a total row underneath them. This is how I want the data to display when returned form my query
This is table structure filled with garbage data, can someone assist me on how to set this up?
Create Table TurribleSetUp
(
ID int,
si varchar(200),
salesmanname varchar(50),
itemsold varchar(100)
)
Insert Into TurribleSetUp Values
(1, 'Home Frank Bad', 'Frank', 'stove'),
(2, 'Internet Frank Left', 'Frank', 'table'),
(3, 'Store Frank Total Store Card', 'Frank', 'stereo')
,(4, 'Store Joe Bad', 'Joe', 'stove'),
(5, 'Store Joe Right', 'Joe', 'stove'),
(6, 'Joe, Person, High Five', 'Joe', 'car')
,(7, 'Frank, Person, High Five', 'Frank', 'car'),
(8, 'Left, Low Five, Joe', 'Joe', 'car')
Try;
select info, total_sale, itemsold piece_of_equipment_sold
from (
select si info, count(itemsold) total_sale, itemsold, salesmanname
from TurribleSetUp
group by si, itemsold, salesmanname
union all
select 'Total for ' + salesmanname, count(itemsold), '' , salesmanname
from TurribleSetUp
group by salesmanname
) x
order by salesmanname, total_sale
I'd do something like this:
select ts.salesmanname, si, count(itemsold) as Cnt, itemsold
from TurribleSetUp ts
group by si, itemsold, ts.salesmanname
union
select salesmanname, 'Totals For '+ Salesmanname as si, count(itemsold) as Cnt, NULL as itemsold
from TurribleSetUp
group by salesmanname
order by ts.salesmanname
I ordered it by Salesman Name. I'm assuming you have some type of number that you'd order it by instead.
Related
I have a table that contains 4 columns - UserID, FromLocation, ToLocation, and Date. I need to pull the "length" of the longest chain (UserID going from FromLocation to ToLocation, as long as the chain does not contain "FAKE_LOCATION").
So, based on the following data set:
CREATE TABLE IF NOT EXISTS `tableA` (
`UserID` int(11) unsigned NOT NULL,
`FromLocation` varchar(20) NOT NULL,
`ToLocation` varchar(20) NOT NULL,
`Date` datetime NOT NULL
) DEFAULT CHARSET=utf8;
INSERT INTO `tableA` (`UserID`, `FromLocation`, `ToLocation`, `Date`) VALUES
(1, 'Loc 1', 'Loc 2', '2022-01-01'),
(1, 'Loc 2', 'Loc 3', '2022-01-02'),
(1, 'Loc 3', 'Loc 5', '2022-01-03'),
(1, 'Loc 5', 'Loc 18', '2022-01-04'),
(1, 'Loc 18', 'Loc 2', '2022-01-05'),
(1, 'Loc 2', 'Loc 4', '2022-01-06'),
(1, 'Loc 4', 'FAKE_LOCATION', '2022-01-07'),
(1, 'FAKE_LOCATION', 'Loc 7', '2022-01-08'),
(1, 'Loc 7', 'Loc 17', '2022-01-09'),
(2, 'Loc 3', 'Loc 4', '2022-01-05'),
(2, 'Loc 4', 'Loc 5', '2022-01-06'),
(2, 'Loc 5', 'FAKE_LOCATIOIN', '2022-01-07'),
(3, 'Loc 3', 'Loc 4', '2022-01-05'),
(3, 'Loc 4', 'FAKE_LOCATIOIN', '2022-01-07'),
(3, 'FAKE_LOCATIOIN', 'Loc 3', '2022-02-07'),
(3, 'Loc 3', 'Loc 5', '2022-02-08');
I'm trying to generate the following data set:
UserID
Longest Chain
1
7
2
3
3
2
For UserID 1, the longest chain is: Loc 1 -> Loc 2 -> Loc 3 -> Loc 5 -> Loc 18 -> Loc 2 -> Loc 4.
For User ID 2, the longest chain is: Loc 3 -> Loc 4 -> Loc 5
For User ID 3, the longest chain is: Loc 3 -> Loc 4. As well as, Loc 3 -> Loc 5
I've created an SQLFiddle for it. Any help shall be appreciated!
Based on the data you provided, I'm assuming that a record that follows another for an ID will always have the same from location as the prior records to location (sorting by UserID and date).
If that's the case, this should solve your problem -
#get max chain count by UserID
SELECT
UserID,
MAX(chain_count) AS LongestChain
FROM(
#count the number of records in each chain, add one for terminal ToLocation for chain
SELECT
UserID,
chained,
COUNT(chained) + 1 AS chain_count
FROM(
#start a new chain every time FAKE_LOCATION is present for a UserID
SELECT
UserID,
FromLocation,
ToLocation,
Date,
(#row_number4:=CASE
WHEN (ToLocation = 'FAKE_LOCATION' OR FromLocation = 'FAKE_LOCATION')
THEN #chain:= #chain + 1
ELSE #chain
END) AS chained
FROM tableA as a, (SELECT #chain:=0,#row_number4:=0) as chain
ORDER BY UserID, Date) chainvalues
WHERE ToLocation != 'FAKE_LOCATION' AND FromLocation != 'FAKE_LOCATION'
GROUP BY UserID, chained) chaincounts
GROUP BY UserID
ORDER BY UserID
;
It was a bit more challenging using MySQL 5.6 than it otherwise could have been since it doesn't allow for window functions.
Here is the fiddle with the query
Here is another example that addresses instances where movement is not necessarily restricted on the next record to the from location for the prior record:
#get max chain count by UserID
SELECT
UserID,
MAX(chain_count) AS LongestChain
FROM(
#count the number of records in each chain, add one for terminal ToLocation for chain
SELECT
UserID,
chained,
COUNT(chained) + 1 AS chain_count
FROM(
#start a new chain every time FAKE_LOCATION is present for a UserID, or b.UserID is null where not the first user record
SELECT
a.UserID,
a.FromLocation,
a.ToLocation,
a.Date,
b.UserID as bUserID,
(#row_number4:=CASE
WHEN (a.ToLocation = 'FAKE_LOCATION' OR a.FromLocation = 'FAKE_LOCATION' OR b.UserID IS NULL) AND userrow != 1
THEN #chain:= #chain + 1
ELSE #chain
END) AS chained,
userrow
FROM (
#get left side of table, userrow increments starting at one for each user instance, num shifts down one to join to b sides to location to ensure date order is followed
SELECT
(#row_number3:=CASE
WHEN #user = UserID
THEN #row_number3 + 1
ELSE 1
END) AS userrow,
#user:=UserID UserID,
FromLocation,
ToLocation,
Date,
(#row_number:=#row_number + 1) - 1 AS num
FROM tableA, (SELECT #row_number:=0) AS t, (SELECT #user:=0,#row_number3:=0) as z
ORDER BY UserID, Date) a
LEFT JOIN (
#right side, effectively shifts tableA up one
SELECT
UserID,
FromLocation,
ToLocation,
Date,
(#row_number2:=#row_number2 + 1) AS num
FROM tableA, (SELECT #row_number2:=0) AS t
ORDER BY UserID, Date) b
ON a.FromLocation = b.ToLocation and a.UserID = b.UserID and a.num = b.num, (SELECT #chain:=0,#row_number4:=0) as chain
ORDER BY a.num) chainvalues
WHERE ToLocation != 'FAKE_LOCATION' AND FromLocation != 'FAKE_LOCATION'
GROUP BY UserID, chained) final
GROUP BY UserID
ORDER BY UserID
;
Here is the fiddle for that example (I added in another record for testing purposes)
I have a problem. How can I write this sql statment without roll up?
I want to get the same result, but without rollup. The result should look like the image below.
my query
select state, city, sum((sales.retail_price - products.wholesale_price) *
sales.quantity) as profit
from products, sales
where sales.product_id = products.product_id
group by state, city WITH ROLLUP
order by state is null, city is null, state, city ;
my schema
-- Create some tables and insert some rows.
create table products (product_id integer, wholesale_price real);
insert into products (product_id, wholesale_price) values
(1, 1.00),
(2, 2.00);
create table sales (product_id integer, retail_price real,
quantity integer, city varchar, state varchar);
insert into sales (product_id, retail_price, quantity, city, state) values
(1, 2.00, 1, 'SF', 'CA'),
(1, 2.00, 2, 'SJ', 'CA'),
(2, 5.00, 4, 'SF', 'CA'),
(2, 5.00, 8, 'SJ', 'CA'),
(2, 5.00, 16, 'Miami', 'FL'),
(2, 5.00, 32, 'Orlando', 'FL'),
(2, 5.00, 64, 'SJ', 'PR');
You must use an UNION operation, as follow:
The first query returns the SUM grouped by state and city, the second only for state, and the third (without group by) for all rows
select state, city, sum((sales.retail_price - products.wholesale_price) *
sales.quantity) as profit
from products, sales
where sales.product_id = products.product_id
group by state, city
UNION ALL
select state, NULL, sum((sales.retail_price - products.wholesale_price) *
sales.quantity) as profit
from products, sales
where sales.product_id = products.product_id
group by state
UNION ALL
select NULL, NULL, sum((sales.retail_price - products.wholesale_price) *
sales.quantity) as profit
from products, sales
where sales.product_id = products.product_id;
I have the following tables:
create table Cars
(
CarID int,
CarType varchar(50),
PlateNo varchar(20),
CostCenter varchar(50),
);
insert into Cars (CarID, CarType, PlateNo, CostCenter) values
(1,'Coupe','BC18341','CALIFORNIA'),
(2,'Hatchback','AU14974','DAKOTA'),
(3,'Hatchback','BC49207','NYC'),
(4,'SUV','AU10299','FLORIDA'),
(5,'Coupe','AU32703','NYC'),
(6,'Coupe','BC51719','CALIFORNIA'),
(7,'Hatchback','AU30325','IDAHO'),
(8,'SUV','BC52018','CALIFORNIA');
create table Invoices
(
InvoiceID int,
InvoiceDate date,
CostCenterAssigned bit,
InvoiceValue money
);
insert into Invoices (InvoiceID, InvoiceDate, CostCenterAssigned, InvoiceValue) values
(1, '2021-01-02', 0, 978.32),
(2, '2021-01-15', 1, 168.34),
(3, '2021-02-28', 0, 369.13),
(4, '2021-02-05', 0, 772.81),
(5, '2021-03-18', 1, 469.37),
(6, '2021-03-29', 0, 366.83),
(7, '2021-04-01', 0, 173.48),
(8, '2021-04-19', 1, 267.91);
create table InvoicesCostCenterAllocations
(
InvoiceID int,
CarLocation varchar(50)
);
insert into InvoicesCostCenterAllocations (InvoiceID, CarLocation) values
(2, 'CALIFORNIA'),
(2, 'NYC'),
(5, 'FLORIDA'),
(5, 'NYC'),
(8, 'DAKOTA'),
(8, 'CALIFORNIA'),
(8, 'IDAHO');
How can I calculate the total invoice values allocated to that car based on its cost center?
If the invoice is allocated to cars in specific cost centers, then the CostCenterAssigned column is set to true and the cost centers are listed in the InvoicesCostCenterAllocations table linked to the Invoices table by the InvoiceID column. If there is no cost center allocation (CostCenterAssigned column is false) then the invoice value is divided by the total number of cars and summed up.
The sample data in Fiddle: http://sqlfiddle.com/#!18/9bd18/3
The data structure here isn't perfect, hence we need some extra code to solve for this. I needed to gather the amount of cars in each location, as well as to allocate the amounts for each invoice, depending on whether or not it was assigned to a location. I broke out the totals for each invoice type so that you can see the components which are being put together, you won't need those in your final result.
;WITH CarsByLocation AS(
SELECT
CostCenter
,COUNT(*) AS Cars
FROM Cars
GROUP BY CostCenter
UNION ALL
SELECT
''
,COUNT(*) AS Cars
FROM Cars
),CostCenterAssignedInvoices AS (
SELECT
InvoicesCostCenterAllocations.CarLocation
,SUM(invoicevalue) / CarsByLocation.cars AS InvoiceTotal
FROM Invoices
INNER JOIN InvoicesCostCenterAllocations ON invoices.InvoiceID = InvoicesCostCenterAllocations.InvoiceID
INNER JOIN CarsByLocation on InvoicesCostCenterAllocations.CarLocation = CarsByLocation.CostCenter
WHERE CostCenterAssigned = 1 --Not needed, put here for clarification
GROUP BY InvoicesCostCenterAllocations.CarLocation,CarsByLocation.Cars
),UnassignedInvoices AS (
SELECT
'' AS Carlocation
,SUM(invoicevalue)/CarsByLocation.Cars InvoiceTotal
FROM Invoices
INNER JOIN CarsByLocation on CarsByLocation.CostCenter = ''
WHERE CostCenterAssigned = 0
group by CarsByLocation.Cars
)
SELECT
Cars.*
,cca.InvoiceTotal AS AssignedTotal
,ui.InvoiceTotal AS UnassignedTotal
,cca.InvoiceTotal + ui.InvoiceTotal AS Total
FROM Cars
LEFT OUTER JOIN CostCenterAssignedInvoices CCA ON Cars.CostCenter = CCA.CarLocation
LEFT OUTER JOIN UnassignedInvoices UI ON UI.Carlocation = ''
ORDER BY
Cars.CostCenter
,Cars.PlateNo;
I want to group by keyword name, get sum of cf1+cf2 (where bug_status=CLOSED or RESOLVED) and get total sum (irrespective of bug status). Output will have 3 columns like mentioned.
Tried query but no luck:
SELECT keyworddefs.name as keyword, IFNULL(SUM(bugs.cf1 + bugs.cf2),0) as completed, (SELECT IFNULL(SUM(bugs.cf1 + bugs.cf2) ,0) FROM bugs, keywords, keyworddefs WHERE (keywords.bug_id = bugs .bug_id) AND (keyworddefs.id=keywords.keywordid) AND (keyworddefs.name LIKE 'K%')) as total FROM bugs, keywords, keyworddefs WHERE (keywords.bug_id = bugs .bug_id) AND (keyworddefs.id=keywords.keywordid) AND (bugs.bug_status = 'VERIFIED' OR bugs.bug_status = 'CLOSED') GROUP BY keyworddefs.name DESC;
Here's the query formatted.
SELECT keyworddefs.name as keyword,
IFNULL(SUM(bugs.cf1 + bugs.cf2),0) as completed,
(SELECT IFNULL(SUM(bugs.cf1 + bugs.cf2) ,0)
FROM bugs, keywords, keyworddefs
WHERE (keywords.bug_id = bugs .bug_id)
AND (keyworddefs.id=keywords.keywordid)
AND (keyworddefs.name LIKE 'K%')) as total
FROM bugs, keywords, keyworddefs
WHERE (keywords.bug_id = bugs .bug_id)
AND (keyworddefs.id=keywords.keywordid)
AND (bugs.bug_status = 'VERIFIED' OR bugs.bug_status = 'CLOSED')
GROUP BY keyworddefs.name DESC;
SQL Fiddle:
http://sqlfiddle.com/#!9/a11b4/7
Expected:
Matching records:
cf1+cf2 bugid, keyword bug_status
5 (102, 'K1') CLOSED
3 (565, 'K2') CLOSED
3 (1352, 'K1') VERIFIED
4 (13634, 'K1') NEW
# Query output should be:
keyword completed total
K1 8 12
K2 3 3
DDLs:
bugs table1 (master table) :
CREATE TABLE `bugs` (
`bug_id` int(11) NOT NULL,
`bug_date` date NOT NULL,
`cf1` int(11) NOT NULL,
`cf2` int(11) NOT NULL,
`bug_status` varchar(200) NOT NULL)
ENGINE=InnoDB DEFAULT CHARSET=latin1;
INSERT INTO `bugs` (`bug_id`, `bug_date`, `cf1`, `cf2`, `bug_status`) VALUES
(102, '2016-07-19', 2, 1, 'CLOSED'),
(72123, '2016-07-19', 2, 1, 'VERIFIED'),
(57234, '2016-07-19', 2, 1, 'VERIFIED'),
(1352, '2016-07-19', 2, 1, 'VERIFIED'),
(565, '2016-07-19', 2, 1, 'CLOSED'),
(13634, '2016-07-22', 2, 2, 'NEW');
keywords table2 (having keyword ids):
CREATE TABLE `keywords` (
`bug_id` int(11) NOT NULL,
`keywordid` varchar(11) NOT NULL)
ENGINE=InnoDB DEFAULT CHARSET=latin1;
INSERT INTO `keywords` (`bug_id`, `keywordid`) VALUES
(102, '3'),
(565, '4'),
(398, '1'),
(565, '2'),
(1352, '1'),
(57234, '2'),
(1363, '1'),
(72123, '2'),
(13634, '3');
keyworddefs table3 (having keyword names according to keywordid):
CREATE TABLE `keyworddefs` (
`id` int(11) NOT NULL,
`name` varchar(200) NOT NULL,
`description` varchar(200) NOT NULL)
ENGINE=InnoDB DEFAULT CHARSET=latin1;
INSERT INTO `keyworddefs` (`id`, `name`, `description`) VALUES
(1, 'J1', 'My J1 item'),
(2, 'J2', 'My J2 item'),
(3, 'K1', 'My K1 item'),
(4, 'K2', 'My K2 item');
How can I get output like expected?
It looks to me like you're making this too complicated.
For one thing, you should join your keywords and keyworddefs tables ON keywords.keywordid = keyworddefs.name. You're using keyworddefs.id. That's a number. So, your old-timey early 1990s comma join yields no results.
For another thing, you don't need to join the keyworddefs table to get your result.
SUM() operations rarely yield NULL results. So, you should put your conditionals inside the parentheses of SUM() rather than outside.
Finally, you need a GROUP BY operation with two SUM() aggregates in it. One should be conditioned on the bug_status and the other should not.
http://sqlfiddle.com/#!9/a11b4/11/0
Something like this should work.
SELECT keywords.keywordid,
SUM(CASE WHEN bugs.bug_status IN ('CLOSED', 'RESOLVED')
THEN bugs.cf1 + bugs.cf2
ELSE 0 END) completed,
SUM(bugs.cf1 + bugs.cf2) total
FROM bugs
JOIN keywords ON bugs.bug_id = keywords.bug_id
GROUP BY keywords.keywordid
ORDER BY keywords.keywordid
If you need to filter your results by keywords.keywordid LIKE 'K%', you can just add a where clause.
Extended query from Ollie's comment, it works fine with couple of changes.
Highly appreciated Ollie!
SELECT keyworddefs.name,
SUM(CASE WHEN bugs.bug_status IN ('CLOSED', 'VERIFIED') THEN bugs.cf1 + bugs.cf2 ELSE 0 END) completed,
SUM(bugs.cf1 + bugs.cf2) total
FROM bugs
JOIN keywords ON bugs.bug_id = keywords.bug_id
JOIN keyworddefs ON keyworddefs.id = keywords.keywordid
WHERE keyworddefs.name LIKE 'K%'
GROUP BY keywords.keywordid
ORDER BY keyworddefs.name DESC;
I am working on a product review page where it will display several submitted reviews as well as the number of comments to each of them.
I thought I could use
SELECT title AS review_title,COUNT(id_group) AS Approved_reply_number
WHERE approved <> '0'
GROUP BY id_group`
but read somewhere that it isn't possible to copy the id values into another row on the insert process. So if someone submits a review, the id_group field for the reviews has to be left empty.
Here is the table example:
CREATE TABLE product_review
(`ID` int, `title` varchar(21), `id_group` int,`approved` int)
;
INSERT INTO product_review
(`ID`, `title`, `id_group`,`approved`)
VALUES
(1, 'AAA', Null,1),
(2, 'BBB', 1,1),
(3, 'CCC', Null,1),
(4, 'DDD', 3,0),
(5, 'EEE', 1,1),
(6, 'FFF', Null,1),
(7, 'GGG', 6,1),
(8, 'HHH',1,1),
(9, 'III', 6,1)
;
Those that are Null in id_group are the submitted reviews. The rest are replies and they contain the id of their corresponding reviews. I was wondering how can I get an output like this:
review_title approved_reply_number
AAA 3
CCC 0
FFF 2
You can use a self join and count query with group by and also a where clause to filter out reviews only
select t.title review_title ,count(*) approved_reply_number
from product_review t
left join product_review t1 on(t.id = t1.id_group)
where t.id_group is null
group by t.id
Demo