Database.php 30 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632
  1. <?php
  2. namespace PhpOffice\PhpSpreadsheet\Calculation;
  3. class Database
  4. {
  5. /**
  6. * fieldExtract.
  7. *
  8. * Extracts the column ID to use for the data field.
  9. *
  10. * @param mixed[] $database The range of cells that makes up the list or database.
  11. * A database is a list of related data in which rows of related
  12. * information are records, and columns of data are fields. The
  13. * first row of the list contains labels for each column.
  14. * @param mixed $field Indicates which column is used in the function. Enter the
  15. * column label enclosed between double quotation marks, such as
  16. * "Age" or "Yield," or a number (without quotation marks) that
  17. * represents the position of the column within the list: 1 for
  18. * the first column, 2 for the second column, and so on.
  19. *
  20. * @return null|string
  21. */
  22. private static function fieldExtract($database, $field)
  23. {
  24. $field = strtoupper(Functions::flattenSingleValue($field));
  25. $fieldNames = array_map('strtoupper', array_shift($database));
  26. if (is_numeric($field)) {
  27. $keys = array_keys($fieldNames);
  28. return $keys[$field - 1];
  29. }
  30. $key = array_search($field, $fieldNames);
  31. return ($key) ? $key : null;
  32. }
  33. /**
  34. * filter.
  35. *
  36. * Parses the selection criteria, extracts the database rows that match those criteria, and
  37. * returns that subset of rows.
  38. *
  39. * @param mixed[] $database The range of cells that makes up the list or database.
  40. * A database is a list of related data in which rows of related
  41. * information are records, and columns of data are fields. The
  42. * first row of the list contains labels for each column.
  43. * @param mixed[] $criteria The range of cells that contains the conditions you specify.
  44. * You can use any range for the criteria argument, as long as it
  45. * includes at least one column label and at least one cell below
  46. * the column label in which you specify a condition for the
  47. * column.
  48. *
  49. * @return array of mixed
  50. */
  51. private static function filter($database, $criteria)
  52. {
  53. $fieldNames = array_shift($database);
  54. $criteriaNames = array_shift($criteria);
  55. // Convert the criteria into a set of AND/OR conditions with [:placeholders]
  56. $testConditions = $testValues = [];
  57. $testConditionsCount = 0;
  58. foreach ($criteriaNames as $key => $criteriaName) {
  59. $testCondition = [];
  60. $testConditionCount = 0;
  61. foreach ($criteria as $row => $criterion) {
  62. if ($criterion[$key] > '') {
  63. $testCondition[] = '[:' . $criteriaName . ']' . Functions::ifCondition($criterion[$key]);
  64. ++$testConditionCount;
  65. }
  66. }
  67. if ($testConditionCount > 1) {
  68. $testConditions[] = 'OR(' . implode(',', $testCondition) . ')';
  69. ++$testConditionsCount;
  70. } elseif ($testConditionCount == 1) {
  71. $testConditions[] = $testCondition[0];
  72. ++$testConditionsCount;
  73. }
  74. }
  75. if ($testConditionsCount > 1) {
  76. $testConditionSet = 'AND(' . implode(',', $testConditions) . ')';
  77. } elseif ($testConditionsCount == 1) {
  78. $testConditionSet = $testConditions[0];
  79. }
  80. // Loop through each row of the database
  81. foreach ($database as $dataRow => $dataValues) {
  82. // Substitute actual values from the database row for our [:placeholders]
  83. $testConditionList = $testConditionSet;
  84. foreach ($criteriaNames as $key => $criteriaName) {
  85. $k = array_search($criteriaName, $fieldNames);
  86. if (isset($dataValues[$k])) {
  87. $dataValue = $dataValues[$k];
  88. $dataValue = (is_string($dataValue)) ? Calculation::wrapResult(strtoupper($dataValue)) : $dataValue;
  89. $testConditionList = str_replace('[:' . $criteriaName . ']', $dataValue, $testConditionList);
  90. }
  91. }
  92. // evaluate the criteria against the row data
  93. $result = Calculation::getInstance()->_calculateFormulaValue('=' . $testConditionList);
  94. // If the row failed to meet the criteria, remove it from the database
  95. if (!$result) {
  96. unset($database[$dataRow]);
  97. }
  98. }
  99. return $database;
  100. }
  101. private static function getFilteredColumn($database, $field, $criteria)
  102. {
  103. // reduce the database to a set of rows that match all the criteria
  104. $database = self::filter($database, $criteria);
  105. // extract an array of values for the requested column
  106. $colData = [];
  107. foreach ($database as $row) {
  108. $colData[] = $row[$field];
  109. }
  110. return $colData;
  111. }
  112. /**
  113. * DAVERAGE.
  114. *
  115. * Averages the values in a column of a list or database that match conditions you specify.
  116. *
  117. * Excel Function:
  118. * DAVERAGE(database,field,criteria)
  119. *
  120. * @category Database Functions
  121. *
  122. * @param mixed[] $database The range of cells that makes up the list or database.
  123. * A database is a list of related data in which rows of related
  124. * information are records, and columns of data are fields. The
  125. * first row of the list contains labels for each column.
  126. * @param int|string $field Indicates which column is used in the function. Enter the
  127. * column label enclosed between double quotation marks, such as
  128. * "Age" or "Yield," or a number (without quotation marks) that
  129. * represents the position of the column within the list: 1 for
  130. * the first column, 2 for the second column, and so on.
  131. * @param mixed[] $criteria The range of cells that contains the conditions you specify.
  132. * You can use any range for the criteria argument, as long as it
  133. * includes at least one column label and at least one cell below
  134. * the column label in which you specify a condition for the
  135. * column.
  136. *
  137. * @return float
  138. */
  139. public static function DAVERAGE($database, $field, $criteria)
  140. {
  141. $field = self::fieldExtract($database, $field);
  142. if ($field === null) {
  143. return null;
  144. }
  145. // Return
  146. return Statistical::AVERAGE(
  147. self::getFilteredColumn($database, $field, $criteria)
  148. );
  149. }
  150. /**
  151. * DCOUNT.
  152. *
  153. * Counts the cells that contain numbers in a column of a list or database that match conditions
  154. * that you specify.
  155. *
  156. * Excel Function:
  157. * DCOUNT(database,[field],criteria)
  158. *
  159. * Excel Function:
  160. * DAVERAGE(database,field,criteria)
  161. *
  162. * @category Database Functions
  163. *
  164. * @param mixed[] $database The range of cells that makes up the list or database.
  165. * A database is a list of related data in which rows of related
  166. * information are records, and columns of data are fields. The
  167. * first row of the list contains labels for each column.
  168. * @param int|string $field Indicates which column is used in the function. Enter the
  169. * column label enclosed between double quotation marks, such as
  170. * "Age" or "Yield," or a number (without quotation marks) that
  171. * represents the position of the column within the list: 1 for
  172. * the first column, 2 for the second column, and so on.
  173. * @param mixed[] $criteria The range of cells that contains the conditions you specify.
  174. * You can use any range for the criteria argument, as long as it
  175. * includes at least one column label and at least one cell below
  176. * the column label in which you specify a condition for the
  177. * column.
  178. *
  179. * @return int
  180. *
  181. * @TODO The field argument is optional. If field is omitted, DCOUNT counts all records in the
  182. * database that match the criteria.
  183. */
  184. public static function DCOUNT($database, $field, $criteria)
  185. {
  186. $field = self::fieldExtract($database, $field);
  187. if ($field === null) {
  188. return null;
  189. }
  190. // Return
  191. return Statistical::COUNT(
  192. self::getFilteredColumn($database, $field, $criteria)
  193. );
  194. }
  195. /**
  196. * DCOUNTA.
  197. *
  198. * Counts the nonblank cells in a column of a list or database that match conditions that you specify.
  199. *
  200. * Excel Function:
  201. * DCOUNTA(database,[field],criteria)
  202. *
  203. * @category Database Functions
  204. *
  205. * @param mixed[] $database The range of cells that makes up the list or database.
  206. * A database is a list of related data in which rows of related
  207. * information are records, and columns of data are fields. The
  208. * first row of the list contains labels for each column.
  209. * @param int|string $field Indicates which column is used in the function. Enter the
  210. * column label enclosed between double quotation marks, such as
  211. * "Age" or "Yield," or a number (without quotation marks) that
  212. * represents the position of the column within the list: 1 for
  213. * the first column, 2 for the second column, and so on.
  214. * @param mixed[] $criteria The range of cells that contains the conditions you specify.
  215. * You can use any range for the criteria argument, as long as it
  216. * includes at least one column label and at least one cell below
  217. * the column label in which you specify a condition for the
  218. * column.
  219. *
  220. * @return int
  221. *
  222. * @TODO The field argument is optional. If field is omitted, DCOUNTA counts all records in the
  223. * database that match the criteria.
  224. */
  225. public static function DCOUNTA($database, $field, $criteria)
  226. {
  227. $field = self::fieldExtract($database, $field);
  228. if ($field === null) {
  229. return null;
  230. }
  231. // reduce the database to a set of rows that match all the criteria
  232. $database = self::filter($database, $criteria);
  233. // extract an array of values for the requested column
  234. $colData = [];
  235. foreach ($database as $row) {
  236. $colData[] = $row[$field];
  237. }
  238. // Return
  239. return Statistical::COUNTA(
  240. self::getFilteredColumn($database, $field, $criteria)
  241. );
  242. }
  243. /**
  244. * DGET.
  245. *
  246. * Extracts a single value from a column of a list or database that matches conditions that you
  247. * specify.
  248. *
  249. * Excel Function:
  250. * DGET(database,field,criteria)
  251. *
  252. * @category Database Functions
  253. *
  254. * @param mixed[] $database The range of cells that makes up the list or database.
  255. * A database is a list of related data in which rows of related
  256. * information are records, and columns of data are fields. The
  257. * first row of the list contains labels for each column.
  258. * @param int|string $field Indicates which column is used in the function. Enter the
  259. * column label enclosed between double quotation marks, such as
  260. * "Age" or "Yield," or a number (without quotation marks) that
  261. * represents the position of the column within the list: 1 for
  262. * the first column, 2 for the second column, and so on.
  263. * @param mixed[] $criteria The range of cells that contains the conditions you specify.
  264. * You can use any range for the criteria argument, as long as it
  265. * includes at least one column label and at least one cell below
  266. * the column label in which you specify a condition for the
  267. * column.
  268. *
  269. * @return mixed
  270. */
  271. public static function DGET($database, $field, $criteria)
  272. {
  273. $field = self::fieldExtract($database, $field);
  274. if ($field === null) {
  275. return null;
  276. }
  277. // Return
  278. $colData = self::getFilteredColumn($database, $field, $criteria);
  279. if (count($colData) > 1) {
  280. return Functions::NAN();
  281. }
  282. return $colData[0];
  283. }
  284. /**
  285. * DMAX.
  286. *
  287. * Returns the largest number in a column of a list or database that matches conditions you that
  288. * specify.
  289. *
  290. * Excel Function:
  291. * DMAX(database,field,criteria)
  292. *
  293. * @category Database Functions
  294. *
  295. * @param mixed[] $database The range of cells that makes up the list or database.
  296. * A database is a list of related data in which rows of related
  297. * information are records, and columns of data are fields. The
  298. * first row of the list contains labels for each column.
  299. * @param int|string $field Indicates which column is used in the function. Enter the
  300. * column label enclosed between double quotation marks, such as
  301. * "Age" or "Yield," or a number (without quotation marks) that
  302. * represents the position of the column within the list: 1 for
  303. * the first column, 2 for the second column, and so on.
  304. * @param mixed[] $criteria The range of cells that contains the conditions you specify.
  305. * You can use any range for the criteria argument, as long as it
  306. * includes at least one column label and at least one cell below
  307. * the column label in which you specify a condition for the
  308. * column.
  309. *
  310. * @return float
  311. */
  312. public static function DMAX($database, $field, $criteria)
  313. {
  314. $field = self::fieldExtract($database, $field);
  315. if ($field === null) {
  316. return null;
  317. }
  318. // Return
  319. return Statistical::MAX(
  320. self::getFilteredColumn($database, $field, $criteria)
  321. );
  322. }
  323. /**
  324. * DMIN.
  325. *
  326. * Returns the smallest number in a column of a list or database that matches conditions you that
  327. * specify.
  328. *
  329. * Excel Function:
  330. * DMIN(database,field,criteria)
  331. *
  332. * @category Database Functions
  333. *
  334. * @param mixed[] $database The range of cells that makes up the list or database.
  335. * A database is a list of related data in which rows of related
  336. * information are records, and columns of data are fields. The
  337. * first row of the list contains labels for each column.
  338. * @param int|string $field Indicates which column is used in the function. Enter the
  339. * column label enclosed between double quotation marks, such as
  340. * "Age" or "Yield," or a number (without quotation marks) that
  341. * represents the position of the column within the list: 1 for
  342. * the first column, 2 for the second column, and so on.
  343. * @param mixed[] $criteria The range of cells that contains the conditions you specify.
  344. * You can use any range for the criteria argument, as long as it
  345. * includes at least one column label and at least one cell below
  346. * the column label in which you specify a condition for the
  347. * column.
  348. *
  349. * @return float
  350. */
  351. public static function DMIN($database, $field, $criteria)
  352. {
  353. $field = self::fieldExtract($database, $field);
  354. if ($field === null) {
  355. return null;
  356. }
  357. // Return
  358. return Statistical::MIN(
  359. self::getFilteredColumn($database, $field, $criteria)
  360. );
  361. }
  362. /**
  363. * DPRODUCT.
  364. *
  365. * Multiplies the values in a column of a list or database that match conditions that you specify.
  366. *
  367. * Excel Function:
  368. * DPRODUCT(database,field,criteria)
  369. *
  370. * @category Database Functions
  371. *
  372. * @param mixed[] $database The range of cells that makes up the list or database.
  373. * A database is a list of related data in which rows of related
  374. * information are records, and columns of data are fields. The
  375. * first row of the list contains labels for each column.
  376. * @param int|string $field Indicates which column is used in the function. Enter the
  377. * column label enclosed between double quotation marks, such as
  378. * "Age" or "Yield," or a number (without quotation marks) that
  379. * represents the position of the column within the list: 1 for
  380. * the first column, 2 for the second column, and so on.
  381. * @param mixed[] $criteria The range of cells that contains the conditions you specify.
  382. * You can use any range for the criteria argument, as long as it
  383. * includes at least one column label and at least one cell below
  384. * the column label in which you specify a condition for the
  385. * column.
  386. *
  387. * @return float
  388. */
  389. public static function DPRODUCT($database, $field, $criteria)
  390. {
  391. $field = self::fieldExtract($database, $field);
  392. if ($field === null) {
  393. return null;
  394. }
  395. // Return
  396. return MathTrig::PRODUCT(
  397. self::getFilteredColumn($database, $field, $criteria)
  398. );
  399. }
  400. /**
  401. * DSTDEV.
  402. *
  403. * Estimates the standard deviation of a population based on a sample by using the numbers in a
  404. * column of a list or database that match conditions that you specify.
  405. *
  406. * Excel Function:
  407. * DSTDEV(database,field,criteria)
  408. *
  409. * @category Database Functions
  410. *
  411. * @param mixed[] $database The range of cells that makes up the list or database.
  412. * A database is a list of related data in which rows of related
  413. * information are records, and columns of data are fields. The
  414. * first row of the list contains labels for each column.
  415. * @param int|string $field Indicates which column is used in the function. Enter the
  416. * column label enclosed between double quotation marks, such as
  417. * "Age" or "Yield," or a number (without quotation marks) that
  418. * represents the position of the column within the list: 1 for
  419. * the first column, 2 for the second column, and so on.
  420. * @param mixed[] $criteria The range of cells that contains the conditions you specify.
  421. * You can use any range for the criteria argument, as long as it
  422. * includes at least one column label and at least one cell below
  423. * the column label in which you specify a condition for the
  424. * column.
  425. *
  426. * @return float
  427. */
  428. public static function DSTDEV($database, $field, $criteria)
  429. {
  430. $field = self::fieldExtract($database, $field);
  431. if ($field === null) {
  432. return null;
  433. }
  434. // Return
  435. return Statistical::STDEV(
  436. self::getFilteredColumn($database, $field, $criteria)
  437. );
  438. }
  439. /**
  440. * DSTDEVP.
  441. *
  442. * Calculates the standard deviation of a population based on the entire population by using the
  443. * numbers in a column of a list or database that match conditions that you specify.
  444. *
  445. * Excel Function:
  446. * DSTDEVP(database,field,criteria)
  447. *
  448. * @category Database Functions
  449. *
  450. * @param mixed[] $database The range of cells that makes up the list or database.
  451. * A database is a list of related data in which rows of related
  452. * information are records, and columns of data are fields. The
  453. * first row of the list contains labels for each column.
  454. * @param int|string $field Indicates which column is used in the function. Enter the
  455. * column label enclosed between double quotation marks, such as
  456. * "Age" or "Yield," or a number (without quotation marks) that
  457. * represents the position of the column within the list: 1 for
  458. * the first column, 2 for the second column, and so on.
  459. * @param mixed[] $criteria The range of cells that contains the conditions you specify.
  460. * You can use any range for the criteria argument, as long as it
  461. * includes at least one column label and at least one cell below
  462. * the column label in which you specify a condition for the
  463. * column.
  464. *
  465. * @return float
  466. */
  467. public static function DSTDEVP($database, $field, $criteria)
  468. {
  469. $field = self::fieldExtract($database, $field);
  470. if ($field === null) {
  471. return null;
  472. }
  473. // Return
  474. return Statistical::STDEVP(
  475. self::getFilteredColumn($database, $field, $criteria)
  476. );
  477. }
  478. /**
  479. * DSUM.
  480. *
  481. * Adds the numbers in a column of a list or database that match conditions that you specify.
  482. *
  483. * Excel Function:
  484. * DSUM(database,field,criteria)
  485. *
  486. * @category Database Functions
  487. *
  488. * @param mixed[] $database The range of cells that makes up the list or database.
  489. * A database is a list of related data in which rows of related
  490. * information are records, and columns of data are fields. The
  491. * first row of the list contains labels for each column.
  492. * @param int|string $field Indicates which column is used in the function. Enter the
  493. * column label enclosed between double quotation marks, such as
  494. * "Age" or "Yield," or a number (without quotation marks) that
  495. * represents the position of the column within the list: 1 for
  496. * the first column, 2 for the second column, and so on.
  497. * @param mixed[] $criteria The range of cells that contains the conditions you specify.
  498. * You can use any range for the criteria argument, as long as it
  499. * includes at least one column label and at least one cell below
  500. * the column label in which you specify a condition for the
  501. * column.
  502. *
  503. * @return float
  504. */
  505. public static function DSUM($database, $field, $criteria)
  506. {
  507. $field = self::fieldExtract($database, $field);
  508. if ($field === null) {
  509. return null;
  510. }
  511. // Return
  512. return MathTrig::SUM(
  513. self::getFilteredColumn($database, $field, $criteria)
  514. );
  515. }
  516. /**
  517. * DVAR.
  518. *
  519. * Estimates the variance of a population based on a sample by using the numbers in a column
  520. * of a list or database that match conditions that you specify.
  521. *
  522. * Excel Function:
  523. * DVAR(database,field,criteria)
  524. *
  525. * @category Database Functions
  526. *
  527. * @param mixed[] $database The range of cells that makes up the list or database.
  528. * A database is a list of related data in which rows of related
  529. * information are records, and columns of data are fields. The
  530. * first row of the list contains labels for each column.
  531. * @param int|string $field Indicates which column is used in the function. Enter the
  532. * column label enclosed between double quotation marks, such as
  533. * "Age" or "Yield," or a number (without quotation marks) that
  534. * represents the position of the column within the list: 1 for
  535. * the first column, 2 for the second column, and so on.
  536. * @param mixed[] $criteria The range of cells that contains the conditions you specify.
  537. * You can use any range for the criteria argument, as long as it
  538. * includes at least one column label and at least one cell below
  539. * the column label in which you specify a condition for the
  540. * column.
  541. *
  542. * @return float
  543. */
  544. public static function DVAR($database, $field, $criteria)
  545. {
  546. $field = self::fieldExtract($database, $field);
  547. if ($field === null) {
  548. return null;
  549. }
  550. // Return
  551. return Statistical::VARFunc(
  552. self::getFilteredColumn($database, $field, $criteria)
  553. );
  554. }
  555. /**
  556. * DVARP.
  557. *
  558. * Calculates the variance of a population based on the entire population by using the numbers
  559. * in a column of a list or database that match conditions that you specify.
  560. *
  561. * Excel Function:
  562. * DVARP(database,field,criteria)
  563. *
  564. * @category Database Functions
  565. *
  566. * @param mixed[] $database The range of cells that makes up the list or database.
  567. * A database is a list of related data in which rows of related
  568. * information are records, and columns of data are fields. The
  569. * first row of the list contains labels for each column.
  570. * @param int|string $field Indicates which column is used in the function. Enter the
  571. * column label enclosed between double quotation marks, such as
  572. * "Age" or "Yield," or a number (without quotation marks) that
  573. * represents the position of the column within the list: 1 for
  574. * the first column, 2 for the second column, and so on.
  575. * @param mixed[] $criteria The range of cells that contains the conditions you specify.
  576. * You can use any range for the criteria argument, as long as it
  577. * includes at least one column label and at least one cell below
  578. * the column label in which you specify a condition for the
  579. * column.
  580. *
  581. * @return float
  582. */
  583. public static function DVARP($database, $field, $criteria)
  584. {
  585. $field = self::fieldExtract($database, $field);
  586. if ($field === null) {
  587. return null;
  588. }
  589. // Return
  590. return Statistical::VARP(
  591. self::getFilteredColumn($database, $field, $criteria)
  592. );
  593. }
  594. }