Related
I have database with ERD:
And sample data:
CREATE TABLE `AppUser` (
`AppUser_ID` bigint(20) NOT NULL,
`SomeFields` varchar(30) COLLATE utf8mb4_bin DEFAULT NULL,
PRIMARY KEY (`AppUser_ID`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_bin;
INSERT INTO `AppUser` (`AppUser_ID`, `SomeFields`) VALUES
(1, 'values'),
(2, 'values'),
(3, 'values'),
(4, 'values'),
(5, 'values');
CREATE TABLE `IdpUser` (
`IdpUser_ID` bigint(20) NOT NULL,
`SomeFields` varchar(30) COLLATE utf8mb4_bin DEFAULT NULL,
ADD PRIMARY KEY (`IdpUser_ID`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_bin;
INSERT INTO `IdpUser` (`IdpUser_ID`, `SomeFields`) VALUES
(1, 'values'),
(2, 'values'),
(3, 'values'),
(4, 'values'),
(5, 'values');
CREATE TABLE `UserClaim` (
`Attribute_ID` bigint(20) NOT NULL,
`IdpUser_ID` bigint(20) DEFAULT NULL,
`AttributeKey` varchar(30) COLLATE utf8mb4_bin DEFAULT NULL,
`AttributeValue` bigint(20) DEFAULT NULL,
PRIMARY KEY (`Attribute_ID`),
FOREIGN KEY (`IdpUser_ID`) REFERENCES `IdpUser` (`IdpUser_ID`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_bin;
INSERT INTO `UserClaim` (`Attribute_ID`, `IdpUser_ID`, `AttributeKey`, `AttributeValue`) VALUES
(1, 1, 'Email_ID', 1),
(2, 2, 'Email_ID', 2),
(3, 3, 'Email_ID', 3),
(4, 4, 'Email_ID', 4),
(5, 5, 'Email_ID', 5),
(6, 2, 'Phone_ID', 4),
(7, 3, 'Phone_ID', 2),
(8, 4, 'Phone_ID', 3),
(9, 5, 'Phone_ID', 5),
(10, 2, 'PublicKey_ID', 1),
(11, 3, 'PublicKey_ID', 2),
(12, 1, 'PublicKey_ID', 1);
CREATE TABLE `UserInfo` (
`Attribute_ID` bigint(20) NOT NULL,
`AppUser_ID` bigint(20) DEFAULT NULL,
`AttributeKey` varchar(30) COLLATE utf8mb4_bin DEFAULT NULL,
`AttributeValue` bigint(20) DEFAULT NULL,
PRIMARY KEY (`Attribute_ID`),
FOREIGN KEY (`AppUser_ID`) REFERENCES `AppUser` (`AppUser_ID`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_bin;
INSERT INTO `UserInfo` (`Attribute_ID`, `AppUser_ID`, `AttributeKey`, `AttributeValue`) VALUES
(1, 1, 'Email_ID', 1),
(2, 2, 'Email_ID', 2),
(3, 3, 'Email_ID', 3),
(4, 4, 'Email_ID', 4),
(5, 5, 'Email_ID', 2),
(6, 2, 'Phone_ID', 1),
(7, 3, 'Phone_ID', 2),
(8, 4, 'Phone_ID', 3),
(9, 5, 'Phone_ID', 4);
CREATE TABLE `UserMapping` (
`Mapping_ID` int(11) NOT NULL,
`AppUser_ID` bigint(20) NOT NULL,
`IdpUser_ID` bigint(20) NOT NULL,
PRIMARY KEY (`Mapping_ID`),
FOREIGN KEY (`AppUser_ID`) REFERENCES `AppUser` (`AppUser_ID`),
FOREIGN KEY (`IdpUser_ID`) REFERENCES `IdpUser` (`IdpUser_ID`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_bin;
INSERT INTO `UserMapping` (`Mapping_ID`, `AppUser_ID`, `IdpUser_ID`) VALUES
(1, 2, 2),
(2, 3, 3),
(3, 1, 1),
(4, 4, 5),
(5, 5, 4);
UserInfo holds internal User info, UserClaim holds external User info, my application has multiple login type. Internal User and External User is mapped.
I use both internal and external User attribute for mapping Users to a Customer. If Users have the same AttributeKey and AttributeValue will be grouped in a Customer and the following attributes of those Users must be added to the Customer
All User with the same AttributeKey and AttributeValue is
grouped into a group.
The Customer will have all attribute of the Users belong to that Customer.
For example - with above data we have Mapping_ID = 2 belongs to Customer1 with list of attributes :
Email_ID = [2]
Phone_ID = [1,4]
PublicKey_ID = [1]
And we also have Mapping_ID = 5 with list of attributes:
Email_ID = [2,5]
Phone_ID = [4,5]
PublicKey_ID = []
And we also have Mapping_ID = 1 with list of attributes:
Email_ID = [1]
Phone_ID = []
PublicKey_ID = [1]
Because Email_ID = 2, Phone_ID = 4 belong to Mapping_ID = [2,5] so Mapping_ID = [2,5] is mapped to Customer1.
Because PublicKey_ID = 1 belong to Mapping_ID = [2,1] so Mapping_ID = [2,1] is mapped to Customer1.
=> Mapping_ID = [1,2,5] are mapped to Customer1 and Customer1 attributes :
Email_ID = [1,2,5]
Phone_ID = [1,4,5]
PublicKey_ID = [1]
My approach is to group by AttributeKey and AttributeValue in each UserInfo and UserClaim to get temporary Customers and their attributes, then join their attributes together and group to a Customer with the same AttributeKey and AttributeValue. I saw my approach made thing difficult to intersect list of attributes to group the Customer and how to store Customer attributes for effectively add or remove attributes.
I am looking for an idea for this problem or any better approach to solve this puzzle.
UserInfo has approximately 100m rows.
UserClaim has approximately 300m rows.
I can use MySql, PDI (Pentaho Data Integration) tools to design and maintain ETL to get my result.
How would you go about to find the real weighted average of multiple rows:
By real weighted, I mean like this calculator: https://www.rapidtables.com/calc/math/weighted-average-calculator.html (and not by multiplying value with weight).
The weight for each answer is set in answers and values for each question in child-table answer_items. We want the weighted average for each question (a,b,c,d). We know what questions to look for in advance.
The query will include between 2 and 500k answers (so preferably a speedy solution :) )
CREATE TABLE `answers` (
`id` int(10) NOT NULL,
`weight` varchar(255) NOT NULL
);
INSERT INTO `answers` (`id`, `weight`) VALUES
(1, '0.7'),
(2, '1'),
(3, '0.7'),
(4, '0.9');
CREATE TABLE `answer_items` (
`id` int(11) NOT NULL,
`answer_id` int(11) NOT NULL,
`question` varchar(5) NOT NULL,
`value` int(11) NOT NULL
);
INSERT INTO `answer_items` (`id`, `answer_id`, `question`, `value`) VALUES
(1, 1, 'a', 2),
(2, 1, 'b', 4),
(3, 1, 'c', 2),
(4, 1, 'd', 3),
(5, 2, 'a', 4),
(6, 2, 'b', 2),
(7, 2, 'c', 4),
(8, 2, 'd', 1),
(9, 3, 'a', 3),
(10, 3, 'b', 4),
(11, 3, 'c', 1),
(12, 3, 'd', 5),
(13, 4, 'a', 5),
(14, 4, 'b', 2),
(15, 4, 'c', 3),
(16, 4, 'd', 3);
ALTER TABLE `answers`
ADD PRIMARY KEY (`id`);
ALTER TABLE `answer_items`
ADD PRIMARY KEY (`id`);
ALTER TABLE `answers`
MODIFY `id` int(10) NOT NULL AUTO_INCREMENT, AUTO_INCREMENT=5;
ALTER TABLE `answer_items`
MODIFY `id` int(11) NOT NULL AUTO_INCREMENT, AUTO_INCREMENT=17;
You can multiply the value times the weight and then divide by the sum of the weights. For the weighted average by question:
select question, sum(ai.value * a.weight) / sum(a.weight)
from answer_items ai join
answers a
on ai.answer_id = a.id
group by question;
Here is a db<>fiddle.
Somethin like:
SELECT
SUM(value * weight) / SUM (weight)
FROM
answers
JOIN
answer_items
ON
answer_items.answer_id = answers.id
(edit: and obviously include the question and GROUP BY question, as in the previous answer for the SUM aggregation, as per the previous answer. D'oh)
I have to write a command that uses the following database:
CREATE TABLE `autori` (
`idAutor` mediumint(8) UNSIGNED NOT NULL AUTO_INCREMENT PRIMARY KEY,
`numeAutor` varchar(255) NOT NULL
);
INSERT INTO `autori` (`idAutor`, `numeAutor`) VALUES
(1, 'Ion Creanga'),
(2, 'Walter Scott'),
(3, 'Mihai Eminescu'),
(4, 'George Bacovia'),
(5, 'William Shakespeare'),
(6, 'Agatha Christie'),
(7, 'Jules Verne');
CREATE TABLE `edituri` (
`idEditura` smallint(5) UNSIGNED NOT NULL AUTO_INCREMENT PRIMARY KEY,
`numeEditura` varchar(255) NOT NULL
);
INSERT INTO `edituri` (`idEditura`, `numeEditura`) VALUES
(1, 'Litera'),
(2, 'CARTEA ROMANEASCA'),
(3, 'MONDORO'),
(4, 'RAO'),
(5, 'PENGUIN BOOKS'),
(6, 'Paralela 45'),
(7, 'CASA EDITORIALA DEMIURG PLUS');
CREATE TABLE `carti` (
`idCarte` int(10) UNSIGNED NOT NULL AUTO_INCREMENT PRIMARY KEY,
`titluCarte` varchar(255) NOT NULL,
`idAutor` mediumint(8) UNSIGNED NOT NULL,
`nrPagini` smallint(5) UNSIGNED DEFAULT NULL,
`ISBN` varchar(17) NOT NULL,
`anPublicare` year(4) DEFAULT NULL,
`idEditura` smallint(5) UNSIGNED NOT NULL,
`stoc` smallint(5) UNSIGNED NOT NULL DEFAULT '0',
`refExterna` varchar(255) DEFAULT NULL,
UNIQUE KEY `isbnUnic` (`ISBN`),
FOREIGN KEY (`idAutor`) REFERENCES `autori` (`idAutor`) ON DELETE CASCADE ON UPDATE CASCADE,
FOREIGN KEY (`idEditura`) REFERENCES `edituri` (`idEditura`) ON DELETE CASCADE ON UPDATE CASCADE
);
INSERT INTO `carti` (`idCarte`, `titluCarte`, `idAutor`, `nrPagini`, `ISBN`, `anPublicare`, `idEditura`, `stoc`, `refExterna`) VALUES
(1, 'Amintiri din copilarie', 1, 48, '978-9975-74-380-8', 2014, 1, 5, NULL),
(2, 'Steaua Sudului', 7, 239, '978-973-23-3185-9', 2017, 2, 2, NULL),
(3, 'Sarpele de mare', 7, 199, '978-973-23-3184-2', 2017, 2, 1, NULL),
(4, '20.000 de leghe sub mari', 7, 300, '978-606-695-008-4', 2014, 3, 9, NULL),
(5, 'Crima din Orient Express', 6, 247, '978-606-609-620-1', 2017, 4, 20, NULL),
(6, 'Poirot investigheaza', 6, 217, '978-606-609-277-7', 2012, 4, 1, NULL),
(7, 'Journey to the Centre of the Earth', 7, 254, '978-0-14-062425-0', 1994, 5, 15, NULL),
(8, 'Cele mai frumoase povesti si povestiri', 1, 211, '978-973-47-2074-3', 2015, 6, 64, NULL),
(9, 'Poezii', 3, 382, '978-606-8395-85-2', 2014, 3, 32, NULL),
(10, 'Hamlet', 5, 228, '978-973-152-006-3', 2007, 7, 13, NULL);
CREATE TABLE `tipuriUtilizatori` (
`idTipUtilizator` tinyint(3) UNSIGNED NOT NULL,
`numeTip` varchar(255) NOT NULL
);
INSERT INTO `tipuriUtilizatori` (`idTipUtilizator`, `numeTip`) VALUES
(1, 'Necunoscut'),
(2, 'Bibliotecar'),
(3, 'Student');
CREATE TABLE `utilizatori` (
`idUtilizator` int(10) UNSIGNED NOT NULL AUTO_INCREMENT PRIMARY KEY ,
`numeUtilizator` varchar(255) NOT NULL,
`idTipUtilizator` tinyint(3) UNSIGNED NOT NULL
);
INSERT INTO `utilizatori` (`idUtilizator`, `numeUtilizator`, `idTipUtilizator`) VALUES
(1, 'Xulescu', 3),
(2, 'Ygrec', 3),
(3, 'Zet', 0),
(4, 'Q', 2);
CREATE TABLE `imprumuturi` (
`idUtilizator` int(10) UNSIGNED NOT NULL,
`idCarte` int(10) UNSIGNED NOT NULL,
UNIQUE (`idUtilizator`,`idCarte`),
FOREIGN KEY (`idUtilizator`) REFERENCES `utilizatori` (`idUtilizator`) ON DELETE CASCADE ON UPDATE CASCADE,
FOREIGN KEY (`idCarte`) REFERENCES `carti` (`idCarte`) ON DELETE CASCADE ON UPDATE CASCADE
) ;
INSERT INTO `imprumuturi` (`idUtilizator`, `idCarte`) VALUES
(2, 3),
(3, 4),
(1, 5);
My command needs to return titluCarte, numeAutor, numeEditura, and also to subtract the rows of imprumuturi table from carti.stoc. I have managed to write this, but the subtracting doesn't return 162-3. What am I doing wrong?
SELECT titlucarte,
numeautor,
numeeditura,
stoc
FROM carti
JOIN autori
ON ( carti.idautor = autori.idautor )
JOIN edituri
ON ( carti.ideditura = edituri.ideditura )
UNION
SELECT '--------------------------------------',
'--------',
'--------',
'----'
UNION
SELECT 'total carti',
'--------',
'--------',
Sum(stoc)
FROM carti
UNION
SELECT '--------------------------------------',
'--------',
'--------',
'----'
UNION
SELECT 'carti disponibile',
'--------',
'--------',
Sum(carti.stoc) - (SELECT Count(*)
FROM imprumuturi)
FROM imprumuturi,
carti;
Every other things works well, my problem is with the subtraction from the end of the command.
If you want 159 change your last select query to below.
SELECT 'carti disponibile',
'--------',
'--------',
Sum(carti.stoc) - (SELECT Count(*) FROM imprumuturi)
FROM carti;
DEMO
The reason you get garbage value in the stoc column is because of the 14th line in your above code. Its expecting a number but you are changing the datatype to string and hence the garbage values.
Try changing ----- to a number like 0 for example and you should get the desired output
See below on sql fiddle
http://sqlfiddle.com/#!9/25e5a1/17
SELECT titlucarte,
numeautor,
numeeditura,
stoc
FROM carti
JOIN autori
ON ( carti.idautor = autori.idautor )
JOIN edituri
ON ( carti.ideditura = edituri.ideditura )
UNION
SELECT '--------------------------------------' as 'titlucarte',
'--------' as 'numeautor',
'--------' as 'numeeditura',
/*'----' as 'stoc' */ /*code removed*/
0 as stoc /* code added*/
UNION
SELECT 'total carti' as 'titlucarte',
'--------' as 'numeautor',
'--------' as 'numeeditura' ,
Sum(stoc) as 'stoc'
FROM carti
UNION
SELECT '--------------------------------------' as 'titlucarte',
'--------' as 'numeautor',
'--------' as 'numeeditura' ,
'----' as 'stoc'
UNION
SELECT 'carti disponibile' as 'titlucarte',
'--------' as 'numeautor',
'--------' as 'numeeditura' ,
Sum(carti.stoc) - (SELECT Count(*)
FROM imprumuturi) as 'stoc'
FROM /*imprumuturi,*/
carti;
I have a problem with my update query. I need to change notify_admin from 0 to 1 only for last users row if action_type = abuse. (Result should be rows with id=9 and id=13)
I'm trying something like that:
UPDATE user_log SET notify_admin = 1
WHERE id IN (
SELECT DISTINCT user_id FROM (SELECT user_id FROM user_log) as UNIKALNE
) AND action_type LIKE 'abuse'
Unfortunately it update only 1 row (id=3).
Here is my table:
CREATE TABLE IF NOT EXISTS `user_log` (
`id` bigint(20) unsigned NOT NULL AUTO_INCREMENT,
`user_id` int(10) unsigned NOT NULL,
`action_type` enum('login','logout','abuse') CHARACTER SET latin1 NOT NULL,
`notify_admin` tinyint(1) NOT NULL DEFAULT '0',
`saved` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP,
PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8 AUTO_INCREMENT=15;
INSERT INTO `user_log` (`id`, `user_id`, `action_type`, `notify_admin`, `saved`) VALUES
(1, 1, 'login', 0, '2015-11-02 12:13:14'),
(2, 1, 'logout', 0, '2015-11-02 13:12:11'),
(3, 1, 'abuse', 0, '2016-01-03 14:10:02'),
(4, 2, 'abuse', 0, '2016-01-04 17:47:03'),
(5, 2, 'login', 0, '2016-01-04 18:11:55'),
(6, 1, 'abuse', 0, '2016-01-04 18:23:57'),
(7, 1, 'abuse', 0, '2016-01-04 18:24:23'),
(8, 2, 'logout', 0, '2016-01-04 18:25:24'),
(9, 1, 'abuse', 0, '2016-01-04 18:25:32'),
(10, 1, 'login', 0, '2016-01-05 21:02:59'),
(11, 3, 'login', 0, '2016-01-05 21:28:43'),
(12, 3, 'logout', 0, '2016-01-05 21:52:01'),
(13, 2, 'abuse', 0, '2016-01-05 22:00:35'),
(14, 1, 'logout', 0, '2016-01-05 22:12:09');
You need to first get the most recent saved value per user and then update the column.
UPDATE user_log
JOIN
(
select id from user_log JOIN (
select user_id,max(saved) max_saved
from user_log
where action_type="abuse"
group by user_id
) t
ON t.user_id = user_log.user_id AND t.max_saved = user_log.saved
) t2
ON user_log.id = t2.id
SET notify_admin = 1
Here you are checking User_id from table with distinct so it will give 1,2,3 only and then comparing with abuse action type so it will update row 3 which matches.
replace User_id with id if you want to update for all rows
UPDATE user_log SET notify_admin = 1 WHERE id IN (SELECT DISTINCT id FROM (SELECT id FROM user_log) as UNIKALNE) AND action_type LIKE 'abuse'
I wish to divide values from the same column using mySQL.
is there a better way of doing this??? Do I really need 3 select statements?
SELECT (SELECT FixAM From Fixes WHERE Id = 1) / (SELECT FixAM From Fixes WHERE Id = 2)
My table structure is as follows:
CREATE TABLE IF NOT EXISTS `Fixes` (
`Id` int(11) NOT NULL AUTO_INCREMENT COMMENT 'PK',
`CurrencyId` int(11) NOT NULL COMMENT 'FK',
`MetalId` int(11) NOT NULL COMMENT 'FK',
`FixAM` decimal(10,5) NOT NULL,
`FixPM` decimal(10,5) DEFAULT NULL,
`TimeStamp` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
PRIMARY KEY (`Id`),
KEY `CurrencyId` (`CurrencyId`),
KEY `MetalId` (`MetalId`)
) ENGINE=InnoDB DEFAULT CHARSET=latin1 COLLATE=latin1_general_ci AUTO_INCREMENT=13 ;
--
-- Dumping data for table `Fixes`
--
INSERT INTO `Fixes` (`Id`, `CurrencyId`, `MetalId`, `FixAM`, `FixPM`, `TimeStamp`) VALUES
(1, 1, 1, '1592.50000', '1586.25000', '2013-02-25 15:10:21'),
(2, 2, 1, '1051.84900', '1049.59300', '2013-02-25 15:10:21'),
(3, 3, 1, '1201.88700', '1194.10600', '2013-02-25 15:10:21'),
(4, 1, 2, '29.17000', NULL, '2013-02-25 13:54:02'),
(5, 2, 2, '19.27580', NULL, '2013-02-25 13:54:02'),
(6, 3, 2, '21.98190', NULL, '2013-02-25 13:54:02'),
(7, 1, 3, '1627.00000', '1620.00000', '2013-02-25 14:28:59'),
(8, 2, 3, '1074.65000', '1072.50000', '2013-02-25 14:28:59'),
(9, 3, 3, '1229.30000', '1218.95000', '2013-02-25 14:28:59'),
(10, 1, 4, '747.00000', '748.00000', '2013-02-25 14:28:59'),
(11, 2, 4, '493.40000', '495.20000', '2013-02-25 14:28:59'),
(12, 3, 4, '564.40000', '562.85000', '2013-02-25 14:28:59');
I think this is what you're looking for:
SELECT MetalId,
MAX(CASE WHEN CurrencyId = 1 THEN FixAM END) /
MAX(CASE WHEN CurrencyId = 2 THEN FixAM ELSE 1 END) Output
FROM Fixes
GROUP BY MetalId
This produces 1592.50000 / 1051.849000. If you want the opposite, swap the currency ids.
SQL Fiddle Demo
In case you don't have a CurrencyId = 2, I defaulted the dividing value to 1 so you wouldn't receive an error.