Related
I get a table check constraint definition this way:
select a.CHECK_CLAUSE
from INFORMATION_SCHEMA.CHECK_CONSTRAINTS a,INFORMATION_SCHEMA.TABLE_CONSTRAINTS b
where b.TABLE_NAME = 'table name'
For my example, suppose running this query returns this:
[([depname]='mathematics' OR [depname]='electronics' OR [depname]='computer science')]
How do I assign the values specified in the check constraint into variables? i.e. computer science, mathematics and electronics?
It looks like you're getting a string returned. What you can do is split the string on instances of OR and store that in an array, and then run through the array and split each element on = to isolate the values. So, if you were to do this in PHP, the code might look something like this:
// For reasons of simplicity we will assume the result is stored in $result,
// and the leading [( and trailing )] have already been removed
$values = array();
$resultsplit = explode(' OR ', $result);
/* $resultsplit is now an array:
* $resultsplit[0] = "[depname]='mathematics'"
* $resultsplit[1] = "[depname]='electronics'"
* $resultsplit[2] = "[depname]='computer science'"
*/
if ($result != '') {
foreach ($resultsplit as $rs) {
$rsparts = explode('=', $rs);
/* $rsparts is now an array. On the first element:
* $rsparts[0] = "[depname]"
* $rsparts[1] = "'mathematics'"
* So all we need to do is stick $rsparts[1] into $values
*/
$values[] = $rsparts[1];
}
}
This will put all of the values into the array $values (including the single-quotes at the beginning and end) for you to do with as you please, regardless of how many there are. If PHP is not the language you have available to you, the same method should still work in your language of choice.
Maybe today's been a long week, but I'm starting to run in circles with trying to figure out the logic on how to solve this.
To use the classic Orders and Items example, we have a webform that tabulates the data of an EXISTING Order e.g. saved in the db.
Now this form needs the ability to add/"mark as removed" ItemIDs from an order after the order has been 'saved'. When the form is submitted, I have two arrays:
$ogList = The original ItemIDs for the OrderID in question. (ex. [123, 456, 789])
$_POST['items'] = The modifications of ItemIDs, if any (ex. [123, 789, 1240, 944])
The intent is to compare the two arrays and:
1) Add new ItemIDs (never have been related to this OrderID before)
2) Mark the date 'removed' those ItemIDs that weren't $_POSTed.
The simple approach of just removing the existing ItemIDs from the Order, and adding the $_POSTed list won't work for business reasons.
PHP's array_diff() doesn't really tell me which ones are "new".
So what's the best way to do this? It appears that I'm looking at a nested foreach() loop ala:
foreach($_POST['items'] as $posted){
foreach($ogList as $ogItem){
if($posted == $ogItem){
Open to ideas here.
}
}
}
...with maybe conditional break(1) in there? Maybe there's a better way?
Another way to perhaps explain is to show the db records. The original in this example would be:
+--------+-------------+
| itemID | dateRemoved |
+--------+-------------+
| 123 | 0 |
| 456 | 0 |
| 789 | 0 |
After the POST, the ItemIDs in this OrderID would look something like:
+--------+-------------+
| itemID | dateRemoved |
+--------+-------------+
| 123 | 0 |
| 456 | 1368029148 |
| 789 | 0 |
| 1240 | 0 |
| 944 | 0 |
Does this make sense? Any suggestions would be appreciated!
EDIT: I found JavaScript sync two arrays (of objects) / find delta, but I'm not nearly proficient enough to translate Javascript and maps. Though it gets me almost there.
Well, when you have items in databse and you receive new dokument with changed set of items, you obviously must update database. One of the easyest way is to delete all previous items and insert new list. But, in case that items is related to other data, you can't do that. So you must apply this technique of comparing two lists. You have items in database, you have items from client after dokument change and with bellow commands you can separate what is for insert in database, what you just need to update and what to delete from database. Below is a simplified example.
$a = [1,2,3,4,5]; //elements in database
$b = [2,3,4,6]; //elements after document save
$del = array_diff($a, $b);
//for delete 1, 5
$ins = array_diff($b, $a);
//for insert 6
$upd = array_intersect($a, $b);
//for update 2, 3, 4
As you can see, this simply compare elements, but if you want to insert real data, you must switch to associative arrays and compare keys. You need to array look something like this:
{
"15":{"employee_id":"1","barcode":"444","is_active":"1"},
"16":{"employee_id":"1","barcode":"555","is_active":"1"},
"17":{"employee_id":"1","barcode":"666","is_active":"1"},
"18":{"employee_id":"1","barcode":"777","is_active":"1"}
}
Here, you have ID extracted from data and placed in array key position.
On server-side, that arrays can be eays get with PDO:
$sth->fetchAll(PDO::FETCH_UNIQUE|PDO::FETCH_ASSOC);
Beware that this will strip the first column from the resultset. So if you want to ID be on both places, use: "SELECT id AS arrkey, id, ... FROM ...".
On server side, when you make array, you don't need all data, just fetch id-s. That all need for compare, because key only matters:
[16=>16, 17=>17, 18=>18, 19=>19, 20=>20];
On client side, with JavaScript, you have to make same structured array of objects so can be compared on server. When you have new items on client, simply use Math.random() to genareate random key, because that doesn't matter.
let cards = {};
cards[Math.random()] = {
employee_id: something,
barcode: something,
is_active: something
}
After that, from client you will get array like this:
{
"16":{"employee_id":"1","barcode":"555","is_active":"1"},
"17":{"employee_id":"1","barcode":"666","is_active":"1"},
"18":{"employee_id":"1","barcode":"777","is_active":"1"},
"0.234456523454":{"employee_id":"1","barcode":"888","is_active":"1"}
}
item with Id 15 is deleted, 16, 17, 18 are changed (or not, you will update them anyway), and new item added. On server you can apply 3 way comparison with: array_diff_key and array_intersect_key.
So, for end, how this server side code looks in my case. First loop is on array keys and secound is to dynamically make UPDATE/INSERT statement.
//card differences
$sql = 'SELECT card_id AS arrkey, card_id FROM card WHERE (employee_id = ?)';
$sth = $this->db->prepare($sql);
$sth->execute([$employee_id]);
$array_database = $sth->fetchAll(PDO::FETCH_UNIQUE|PDO::FETCH_ASSOC);
$array_client = json_decode($_POST['...'], true);
//cards update
foreach (array_intersect_key($array_database, $array_client) as $id => $data) {
$query = 'UPDATE card SET';
$updates = array_filter($array_client[$id], function ($value) {return null !== $value;}); //clear nulls
$values = [];
foreach ($updates as $name => $value) {
$query .= ' '.$name.' = :'.$name.',';
$values[':'.$name] = $value;
}
$query = substr($query, 0, -1); // remove last comma
$sth = $this->db->prepare($query . ' WHERE (card_id = ' . $id . ');');
$sth->execute($values);
}
//cards insert
foreach (array_diff_key($array_client, $array_database) as $id => $card) {
$prep = array();
foreach($card as $k => $v ) {
$prep[':'.$k] = $v;
}
$sth = $this->db->prepare("INSERT INTO card ( " . implode(', ',array_keys($card)) . ") VALUES (" . implode(', ',array_keys($prep)) . ")");
$sth->execute($prep);
}
//cards delete
foreach (array_diff_key($array_database, $array_client) as $id => $data) {
$sth = $this->db->prepare('DELETE FROM card WHERE card_id = ?');
$sth->execute([$id]);
}
My requirement is, I want to map some checklist values to some groups. The following is my code:
#selectbox1 => contains the selected select groups
#selectbox2 => contains selected checklist
Code:
foreach $select1(#selectbox1) {
my $sql_select1 = "select id from group_management where group_name = '$select1'";
my $box1 = $dbslave -> prepare($sql_select1);
$box1 -> execute();
while($select_box1= $box1->fetchrow_array())
{
push (#box1,$select_box1);
}
my $box_1 = #box1; # currently I tried like this to store the current value .NEED CORRECTION HERE
foreach $select2(#selectbox2) {
my $sql_select2 = "select id from checklist where checklist_name = '$select2'";
my $box2 = $dbslave -> prepare($sql_select2);
$box2 -> execute();
while($select_box2 = $box2->fetchrow_array())
{
push (#box2,$select_box2);
}
my $box_2 = #box2; # currently I tried like this to store the current value .NEED CORRECTION HERE
my $sql_insert = "insert into checklist_group_mapping values ('',$box_2,$box_1)";
my $ins = $dbslave -> prepare($sql_insert);
$ins -> execute();
}
}
How can I assign the current value of the array to a variable so that I can insert it into the mapping table?
You need to read up on 'context', and in particular 'scalar context' and 'array context'.
When you write:
my $box_1 = #box1;
you are providing scalar context, and in scalar context, #box1 returns the number of elements in the array. If you wrote:
my($box_1) = #box1;
you would be providing array context, and in array context, the first element of #box1 would be assigned to the first element of the array context, $box_1 — and the remaining elements of #box1 would be dropped. (This may well be what you're after; it is likely that you are trying to select the single ID value for each of the various names in #selectbox1.)
Judging from how you're trying to use the $box_1 and $box_2 variables in your code, you are looking to obtain a single string containing all the values from #box1 and another single string containing all the values from #box2, and they probably need to be presented to the DBI driver enclosed in single quotes.
You can get space-separated values into a string using:
my $box_1 = "#box1";
If you need comma-separated values, you can use:
my $box_1;
{ local $" = ","; $box_1 = "#box_1"; }
The $" (aka $LIST_SEPARATOR under use English '-no_match_vars';) must be localized to prevent damage, but that means you have to separate the definition of $box_1 from the assignment (because if you don't, $box_1 is destroyed when you leave the {...} block).
Now, to protect that so that the SQL can work, you need to use the quote method:
$box1 = $dbslave->quote($box1);
or:
my $box1 = $dbslave->quote("#box1");
Assembling these changes, we get:
#!/usr/bin/env perl
use strict;
use warnings;
### Improved, but not operational
# use DBI;
my #selectbox1 = ( "group1", "group2", "group3" );
my #selectbox2 = ( "check1", "check2", "check3" );
my $dbslave;
# $dbslave = DBI->connect(...) or die "A horrible death";
foreach my $select1 (#selectbox1)
{
my $sql_select1 = "select id from group_management where group_name = '$select1'";
my $box1 = $dbslave->prepare($sql_select1);
$box1->execute();
my #box1;
while (my $select_box1 = $box1->fetchrow_array())
{
push #box1, $select_box1;
}
my $box_1 = $dbslave->quote("#box1");
foreach my $select2(#selectbox2)
{
my $sql_select2 = "select id from checklist where checklist_name = '$select2'";
my $box2 = $dbslave->prepare($sql_select2);
$box2->execute();
my #box2;
while (my $select_box2 = $box2->fetchrow_array())
{
push #box2, $select_box2;
}
my $box_2 = $dbslave->quote("#box2");
my $sql_insert = "insert into checklist_group_mapping values ('', $box_2, $box_1)";
my $ins = $dbslave->prepare($sql_insert);
$ins->execute();
}
}
Note that the two SELECT statements assume that the select box strings contain no funny characters (specifically, no single quotes). If you're in charge of the content of #selectbox1 and #selectbox2, that's OK. If they contain user input, you have to sanitize that input, or use $dbslave->quote() again, or use place-holders. I'm going to ignore the issue.
You are also using scalar context with $box1->fetchrow_array(), which is not going to yield the answer you want (although fetchrow_array() is context sensitive, the manual warns you to be careful). I would use something like:
my #box1;
while (my #row = $box1->fetchrow_array())
{
push #box1, $row[0];
}
my $box_1 = $dbslave->quote("#box1");
You also need to use functions. There's a glaring repeat in your code that can be encapsulated into a single function used twice:
#!/usr/bin/perl
use strict;
use warnings;
# use DBI;
my #selectbox1 = ( "group1", "group2", "group3" );
my #selectbox2 = ( "check1", "check2", "check3" );
my $dbslave;
# $dbslave = DBI->connect(...) or die "A horrible death";
sub fetch_all
{
my($dbh, $sql) = #_;
my $sth = $dbh->prepare($sql);
$sth->execute();
my #results;
while (my #row = $sth->fetchrow_array())
{
push #results, $row[0];
}
my $result = $dbslave->quote("#results");
return $result;
}
foreach my $select1 (#selectbox1)
{
my $sql_select1 = "select id from group_management where group_name = '$select1'";
my $box_1 = fetch_all($dbslave, $sql_select1);
foreach my $select2(#selectbox2)
{
my $sql_select2 = "select id from checklist where checklist_name = '$select2'";
my $box_2 = fetch_all($dbslave, $sql_select2);
my $sql_insert = "insert into checklist_group_mapping values ('', $box_2, $box_1)";
my $ins = $dbslave->prepare($sql_insert);
$ins->execute();
}
}
The INSERT statement should be converted to use placeholders so it can be prepared once and used many times:
my $sql_insert = "insert into checklist_group_mapping values ('', ?, ?)";
my $ins = $dbslave->prepare($sql_insert);
foreach my $select1 (#selectbox1)
{
my $sql_select1 = "select id from group_management where group_name = '$select1'";
my $box_1 = fetch_all($dbslave, $sql_select1);
foreach my $select2(#selectbox2)
{
my $sql_select2 = "select id from checklist where checklist_name = '$select2'";
my $box_2 = fetch_all($dbslave, $sql_select2);
$ins->execute($box_1, $box_2);
}
}
Indeed, the two SELECT statements should also be parameterized and prepared once and reused. I've not shown that change because (a) I'm lazy and (b) there's a bigger change that is still more effective.
When we look at what you're really doing, it should all be a single SQL statement:
#!/usr/bin/perl
use strict;
use warnings;
# use DBI;
my #selectbox1 = ( "group1", "group2", "group3" );
my #selectbox2 = ( "check1", "check2", "check3" );
my $dbslave;
# $dbslave = DBI->connect(...) or die "A horrible death";
sub placeholder_list
{
my($n) = #_;
die "$n should be larger than 0" if $n <= 0;
my $list = "(?" . ",?" x ($n - 1) . ")";
return $list;
}
my $sql_insert = qq%
INSERT INTO checklist_group_mapping(col1, col2, col3)
SELECT '', gm.id, cl.id
FROM group_management AS gm
CROSS JOIN checklisst AS cl
WHERE gm.group_name IN X1
AND cl.checklist_name IN X2
%;
my $X1 = placeholder_list(scalar(#selectbox1));
my $X2 = placeholder_list(scalar(#selectbox2));
$sql_insert =~ s/X1/$X1/;
$sql_insert =~ s/X2/$X2/;
my $ins = $dbslave->prepare($sql_insert);
$ins->execute(#selectbox1, #selectbox2);
The big advantage of this is that there are far fewer round trips for information flowing between the application and the database, which (almost) invariably improves performance, often dramatically.
The only residual issue is whether your DBMS supports explicit CROSS JOIN like that. If not, you'll need to replace the words CROSS JOIN with a single comma.
There are still things that should be fixed, such as checking that the prepared statements were successfully prepared, and so on. But this may have given you some insight into how to think about using the DBI with Perl.
The trick is to use $_ variable inside your foreach. Like this:
my $current_value;
foreach $select2(#selectbox2) {
$current_value = $_;
my $sql_select2 = "select id from checklist where checklist_name = '$select2'";
......
my $box_2 = $current_value;
I have the following array of references to arrays:
my #holidays = [[2012,'01','02'],[2012,'01','16'],[2012,'02','20'],[2012,'04','16'],[2012,'05','28'],[2012,'07','04'],[2012,'09','03'],[2012,'10','08'],[2012,'11','12'],[2012,'11','22'],[2012,'12','25']];
Which are IRS recognized legal holidays during 2012. I would like to match the array #dueDate to a value in that array and return 1 or true if it is present.
while ($holidays[#dueDate]){
print ("Found Holiday \t join('-',#dueDate)");
#dueDate = Add_Delta_Days(#dueDate, 1);
if ( Day_of_Week(#dueDate) > 5){
#dueDate = Monday_of_Week((Week_Number(#dueDate)+1), $dueDate[0]);
}
}
Is my current attempt at this - the condition of the while statement is never true. I've tried a few different combinations of referencing and dereferencing holidays to no avail.
What would the best way be to manipulate the evaluation within the while statement such that the block executes when #dueDate contains a date within my array above.
Note: #dueDate is a Date::Calc standard array - (Year, Month, Day)
This should put you on the right track. Two problems I see with your code - an array of arrays should have normal parentheses on the outer part, and use the ~~ operator to compare arrays for equality.
my #holidays = ([2012,'01','02'],[2012,'01','16'],[2012,'02','20'],[2012,'04','16'],
[2012,'05','28'],[2012,'07','04'],[2012,'09','03'],[2012,'10','08'],[2012,'11','12'],
[2012,'11','22'],[2012,'12','25']);
my $i;
my #duedate = [2012, '01', '02'];
for ($i = 0; $i < #holidays; $i++)
{
if (#holidays[$i] ~~ #duedate)
{
print "matched!!";
}
}
First,
my #holidays = [[2012,'01','02'],...,[2012,'12','25']];
should be
my #holidays = ([2012,'01','02'],...,[2012,'12','25']);
You're creating an array with a single element.
Probably the best way to achieve what you want is to use a hash.
my %holidays = map { join('-', #$_) => 1 } #holidays;
Then all you need is
while ($holidays{join('-', #dueDate)}) {
my $dow = Day_of_Week(#dueDate);
#dueDate = Add_Delta_Days(#dueDate,
$dow == 5 || $dow == 6 ? 8 - $dow : 1);
}
This is my answer, working on Perl 5.14, also I use smartmatching ~~ operator to compare two arrays.
You assign to array #holidays = [[2012,'01','02'], ]; isn't correct actually you assign anonymous array [ ['2012', '01', '02'], ] to first element of #holidays.
use v5.14;
my #holidays = ( ['2012', '01', '02'], ['2012', '01', '16'] );
my #due_date = ( '2012', '01', '16' );
for my $holiday (#holidays) {
if (#$holiday ~~ #due_date) {
say "holiday match";
}
}
Okay, a few things:
1: Lists are contained in parentheses, and literal array references are written between brackets. So, you should have:
my #holidays = ([2012,'01','02'],[2012,'01','16'],[2012,'02','20'],[2012,'04','16'],[2012,'05','28'],
[2012,'07','04'],[2012,'09','03'],[2012,'10','08'],[2012,'11','12'],[2012,'11','22'],[2012,'12','25']);
2: When you look at $holidays[#dueDate], you're calling everything in scalar context. In particular, since #dueDate has three elements, you're only looking at $holidays[3].
3: Unless you're writing a piece of throwaway code, always use strict; and use warnings;.
So, you want something like this:
use strict;
use warnings;
my #holidays = ([2012,'01','02'],[2012,'01','16'],[2012,'02','20'],[2012,'04','16'],[2012,'05','28'],
[2012,'07','04'],[2012,'09','03'],[2012,'10','08'],[2012,'11','12'],[2012,'11','22'],[2012,'12','25']);
my #dueDates=([2012,'01','01'],[2012,'01','02'],[2012,'01','03']); #Or whatever
my #due_dates_that_are_holidays=();
foreach my $due_date(#dueDates)
{
foreach my $holiday(#holidays)
{
my ($h_y,$h_m,$h_d)=#$holiday; #Capturing year month and day from the array reference
my ($d_y,$d_m,$d_d)=#$due_date; #Ditto for the due date
if($h_y == $d_y and $h_m eq $d_m and $h_d eq $d_d)
{
push #due_dates_that_are_holidays,$due_date;
}
}
}
print join("-",#{$_}) . "\n" foreach(#due_dates_that_are_holidays);
The above code produces the following output:
2012-01-02
I am a bit new to Perl and I need some help regarding moving my Hash of Arrays across Modules.
Currently I have a db module that stores an array like so:
sub getSourceCriteria {
my($self) = shift();
my($sourceId) = shift();
chomp $sourceId;
my(%criteria) =();
$logger->debug("Getting records for Source ID: " . $sourceId);
$dbh=DBI->connect('dbi:ODBC:StkSkrnDB', 'RTETET', 'XXuser01',{ RaiseError => 1, AutoCommit => 0 }) || \
$logger->err_die("Database connection not made: $DBI::errstr\n");
my($sth) = "select a.criteria_id, a.criteria_type, a.criteria_props,a.generalcriteria_id,b.field_id ";
$sth = $sth . "from t_criteria a, t_sourceMapping b where a.generalcriteria_id = (select generalcriteria_id from t_sourcecriteria where source_id =?) ";
$sth = $sth . "and a.criteria_id=b.criteria_id";
my($qry) = $dbh->prepare($sth);
$qry->execute($sourceId) || $logger->error("Could not query for Source Criteria: $DBI::errstr\n");
my(#row)=();
my($tempCount) = 0;
while ( #row = $qry->fetchrow_array ) {
$tempCount = scalar #row;
$logger->debug("Size of retrieved SQL Array : $tempCount");
$criteria{$row[0]} = \#row;
###{$criteria{$row[0]} } = \#row;
}
return %criteria;
}
And I have a seperate perl script that reads the SQL output from the code above:
foreach my $criteria (keys %criterias) {
#temp = exists( $criterias{$criteria} ) ? #{ $criterias{$criteria} } : ();
##my $tempStr = exists( $criterias{$criteria} ) ? "Yes" : "No";
$arraySize = scalar #temp;
$logger->debug("GENERALCRITERIA_ID is $GENERALCRITERIA_ID and size of array is $arraySize and $temp[0]");
$genCrit_ID = $temp[$GENERALCRITERIA_ID];
$logger->debug("Criteria ID $criteria has Gen Criteria ID $genCrit_ID");
if (0!=$generalCriteria_ID || $generalCriteria_ID != $genCrit_ID ) { ## test for uniqueness
$generalCriteria_ID = -1;
}
else {
$generalCriteria_ID = $genCrit_ID;
}
}# do something with $key and $value
$generalCriteria = $generalCriteria_ID;
}
The problem is I keep getting 0 as the retrieved array size( 2nd snippet) even though when I store the array ( in the 1st snippet ) I check and get the actual array size.
Please any help/clarification would be greatly appreciated.
EDIT
Added more code in the DB interface code.
In your while loop, you are assigning to #row and then storing a reference to that array. However, each time the loop iterates, you are replacing the contents of #row without declaring a new array. So at the end, each of your references point towards the same thing.
In your code here:
my(#row)=();
my($tempCount) = 0;
while ( #row = $qry->fetchrow_array ) {
$tempCount = scalar #row;
$logger->debug("Size of retrieved SQL Array : $tempCount");
$criteria{$row[0]} = \#row;
###{$criteria{$row[0]} } = \#row;
}
Each time the while loop iterates, you assign new values to the #row array. But since the my(#row)=(); line occurs outside of the loop, the #row array is always the same. So each time you assign to the array, you are changing what is stored in all of the references you have already taken.
To fix the problem, you need to declare a new array for each iteration. The simplest way to do this is to move the declaration into the while condition:
my($tempCount) = 0;
while ( my #row = $qry->fetchrow_array ) {
$tempCount = scalar #row;
$logger->debug("Size of retrieved SQL Array : $tempCount");
$criteria{$row[0]} = \#row;
###{$criteria{$row[0]} } = \#row;
}
Now each time you take the reference \#row you will be getting a reference to a new array.
If your $qry->fetchrow_array method returned an array reference, you would not have had the issue:
my $row;
while ($row = $qry->fetchrow_array) {
$logger->debug("Size of retrieved SQL Array : ".#$row);
$criteria{$$row[0]} = $row; # already a reference
}
But I would still write that as while (my $row = ... in my own code, since keeping scopes small is a good thing.