SQL - why would this not work? - sql

The following script allows you to create the database and populate it with necessary data for my question:
# HeidiSQL Dump
#
# --------------------------------------------------------
# Host: 127.0.0.1
# Database: blueskylearning
# Server version: 5.1.22-rc-community
# Server OS: Win32
# Target-Compatibility: MySQL 5.0
# Extended INSERTs: Y
# max_allowed_packet: 1048576
# HeidiSQL version: 3.0 Revision: 572
# --------------------------------------------------------
/*!40100 SET CHARACTER SET latin1*/;
#
# Database structure for database 'windowsLinuxProblem'
#
CREATE DATABASE /*!32312 IF NOT EXISTS*/ `windowsLinuxProblem` /*!40100 DEFAULT CHARACTER SET utf8 */;
USE `windowsLinuxProblem`;
#
# Table structure for table 'organization'
#
CREATE TABLE /*!32312 IF NOT EXISTS*/ `organization` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`name` varchar(200) NOT NULL,
PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=4 DEFAULT CHARSET=utf8;
#
# Dumping data for table 'organization'
#
/*!40000 ALTER TABLE `organization` DISABLE KEYS*/;
LOCK TABLES `organization` WRITE;
REPLACE INTO `organization` (`id`, `name`) VALUES
(1,'Org1');
UNLOCK TABLES;
/*!40000 ALTER TABLE `organization` ENABLE KEYS*/;
#
# Table structure for table 'resource'
#
CREATE TABLE /*!32312 IF NOT EXISTS*/ `resource` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`title` varchar(100) NOT NULL,
`resourcePublic` tinyint(4) NOT NULL,
`active` tinyint(4) NOT NULL,
`published` tinyint(4) NOT NULL,
PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=534 DEFAULT CHARSET=utf8;
#
# Dumping data for table 'resource'
#
/*!40000 ALTER TABLE `resource` DISABLE KEYS*/;
LOCK TABLES `resource` WRITE;
REPLACE INTO `resource` (`id`, `title`, `resourcePublic`, `active`, `published`) VALUES
(1,'Title number 1',1,1,1),
(2,'Title number 2',1,1,1),
(3,'Title number 3',1,1,1),
(4,'Title number 4',1,1,1),
(5,'Title number 5',1,1,1),
(6,'Title number 6',1,1,1);
UNLOCK TABLES;
/*!40000 ALTER TABLE `resource` ENABLE KEYS*/;
#
# Table structure for table 'resourceorganization'
#
CREATE TABLE /*!32312 IF NOT EXISTS*/ `resourceorganization` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`resource_id` int(11) NOT NULL,
`organization_id` int(11) NOT NULL,
`forever` tinyint(4) NOT NULL,
`startDate` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
`endDate` timestamp NOT NULL DEFAULT '0000-00-00 00:00:00',
PRIMARY KEY (`id`),
KEY `resource_id` (`resource_id`),
KEY `organization_id` (`organization_id`),
CONSTRAINT `resourceorganization_ibfk_1` FOREIGN KEY (`resource_id`) REFERENCES `resource` (`id`),
CONSTRAINT `resourceorganization_ibfk_2` FOREIGN KEY (`organization_id`) REFERENCES `organization` (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=115 DEFAULT CHARSET=utf8;
Could someone help me understand why the following query does not fetch all the resources with active=1, published=1, resourcePublic=1
Query:
select *
from
resource resource0_,
resourceOrganization resourceor1_
where
resource0_.active=1
and resource0_.published=1
and (
resource0_.resourcePublic=1
or resourceor1_.resource_id=resource0_.id
and resourceor1_.organization_id=2
and (
resourceor1_.resource_id=resource0_.id
and resourceor1_.forever=1
or resourceor1_.resource_id=resource0_.id
and (
current_date between resourceor1_.startDate and resourceor1_.endDate
)
)
)

As it stands, this query should simplify to:
select *
from
resource resource0_,
resourceOrganization resourceor1_
where
resource0_.active=1 and
resource0_.published=1 and
(resource0_.resourcePublic=1 or
(resourceor1_.resource_id=resource0_.id and
resourceor1_.organization_id=2 and
(resourceor1_.forever=1 or
current_date between resourceor1_.startDate and resourceor1_.endDate
)
)
)
I would expect this to produce a cartesian join to all records on the resourceOrganization table, where the resource table's active, published and resourcePublic values are all 1.
Where the resource table's active and published values are both 1, but resourcePublic is not 1, I would expect an inner join to records on the resourceOrganization table where the organization_id is 2, and either forever is 1 or the current date as at midnight is between the start and end datetime values. I note that the end datetime defaults to 0000-00-00 00:00:00.
Excluding records where either active or published values are not 1, the obvious reasons for the resource not being reported are therefore that the resourcePublic values are not 1, and either:
there is no corresponding resourceOrganization record for organization_id 2, or
the corresponding resourceOrganization record has a forever value that is not 1 and
the current date as at midnight is not between the start and end datetime values.
The date range appears the most likely source of incorrectly excluded records - records entered in the current day will have start datetime values after midnight and will therefore be excluded, while records with no end date specified will default to 0000-00-00 00:00:00 and will therefore also be excluded.
Therefore, I recommend rewriting the query to be something like:
select *
from resource r
left join resourceOrganization ro
on r.id = ro.resource_id and ro.organization_id = 2
where
r.active=1 and
r.published=1 and
(r.resourcePublic=1 or
ro.forever=1 or
now() between ro.startDate and
(case where ro.endDate = '0000-00-00 00:00:00'
then now()
else ro.endDate end
)
)

Mark, Tony, Leslie and all, thanks for your feedback but I managed to sidestep the problem!
I discovered that the where clause results in false if there are no records in resourceOrganization table. That means even if resource table has records where active, published and resourcePublic are all 1, if there are no data in resourceOrganization table, the where clause results in false. I really don't understand why this is the case but now that I discovered this, I added some logic in my Java code to build query string depending on whether are records in resourceOrganization table.
In any case, does anyone have any explanation for this behavior?

Related

Upsert (merge) for updating record if it exists and inserting otherwise

I am trying to write a DB2 query that allows me to either update a record if it already exists but if it does not exist it should be inserted. I wrote the following query that should accomplish this:
MERGE INTO OA1P.TLZ712A1 AS PC
USING (
SELECT * FROM OA1P.TLZ712A1
WHERE CALENDAR_ID=13 AND
"PACKAGE"='M2108'
) PC2
ON (PC.ID_PACKAGE_CALENDAR=PC2.ID_PACKAGE_CALENDAR)
WHEN MATCHED THEN
UPDATE SET ACT_DATE = '31.12.2021'
WHEN NOT MATCHED THEN
INSERT ("PACKAGE", ACT_DATE, CALENDAR_ID, PREPTA, MIXED) VALUES ('M2108', '31.12.2021', 13, 0, 0)
This query should attempt to check if a record already exists for the selection criteria. Updating a record seems to be working fine but I am not able to get the "WHEN NOT MATCHED" part to work and inserting a new record. Anyone able to provide some assistance?
The table is used to save the activation date of a certain software package. PACKAGE is the reference to the package table containing the name of the package (eg. "M2108"). CALENDAR_ID refers to a system where the software package will be activated. The actual date is stored in ACT_DATE.
Did not manage to get the DDL into SQLFiddle so I have to provide it here:
CREATE TABLE OA1P.TLZ712A1 (
ID_PACKAGE_CALENDAR INTEGER DEFAULT IDENTITY GENERATED BY DEFAULT NOT NULL,
CALENDAR_ID INTEGER,
"PACKAGE" VARCHAR(10) NOT NULL,
ACT_DATE DATE NOT NULL,
PREPTA SMALLINT DEFAULT 0 NOT NULL,
MIXED SMALLINT DEFAULT 0 NOT NULL,
"COMMENT" VARCHAR(60) NOT NULL,
LAST_MODIFIED_PID CHAR(7) NOT NULL,
ST_STARTID TIMESTAMP NOT NULL,
ST_FROM TIMESTAMP NOT NULL,
ST_TO TIMESTAMP NOT NULL,
CONSTRAINT TLZ712A1_PK PRIMARY KEY (ID_PACKAGE_CALENDAR),
CONSTRAINT CALENDAR FOREIGN KEY (CALENDAR_ID) REFERENCES OA1P.TLZ711A1(ID_CALENDAR) ON DELETE RESTRICT,
CONSTRAINT "PACKAGE" FOREIGN KEY ("PACKAGE") REFERENCES OA1P.TLZ716A1(NAME) ON DELETE RESTRICT
);
CREATE UNIQUE INDEX ILZ712A0 ON OA1P.TLZ712A1 (ID_PACKAGE_CALENDAR);
If your goal is to set ACT_DATE to 31.12.2021 if a row is found with PACKAGE = M2108 and CALENDAR_ID = 13 and if no row is found with these values then insert it, then this could be the answer
MERGE INTO OA1P.TLZ712A1 AS PC
USING (
VALUES ('M2108', 13, date '31.12.2021')
) PC2 ("PACKAGE", CALENDAR_ID, ACT_DATE)
ON (PC."PACKAGE", PC.CALENDAR_ID) = (PC2."PACKAGE", PC2.CALENDAR_ID)
WHEN MATCHED THEN
UPDATE SET ACT_DATE = PC2.ACT_DATE
WHEN NOT MATCHED THEN
INSERT ("PACKAGE", ACT_DATE, CALENDAR_ID, PREPTA, MIXED) VALUES (PC2."PACKAGE", PC2.ACT_DATE, PC2.CALENDAR_ID, 0, 0)

Constraint violation when merging into table

I'm having a staging table and a datawarehouse table, which keep giving me constraint violation. i can't seem to figure out why since DRIVERID and RACEID a combination of those should be unique? How come i get contraint violation - primary key
table
CREATE TABLE QUALIFYING (
QUALIFYID DECIMAL(18,0) IDENTITY NOT NULL,
RACEID DECIMAL(18,0) DEFAULT '0' NOT NULL,
DRIVERID DECIMAL(18,0) DEFAULT '0' NOT NULL,
CONSTRUCTORID DECIMAL(18,0) DEFAULT '0' NOT NULL,
DRIVERNUMBER DECIMAL(18,0) DEFAULT '0' NOT NULL,
DRIVERPOSITION DECIMAL(18,0) DEFAULT NULL,
Q1 VARCHAR(255) UTF8 DEFAULT NULL,
Q2 VARCHAR(255) UTF8 DEFAULT NULL,
Q3 VARCHAR(255) UTF8 DEFAULT NULL,
PRIMARY KEY(QUALIFYID)
);
Staging
CREATE OR REPLACE TABLE STGQUALIFYING(
raceId int DEFAULT '0' NOT NULL,
driverId int DEFAULT '0' NOT NULL,
constructorId int DEFAULT '0' NOT NULL,
driverNumber int DEFAULT '0' NOT NULL,
driverPosition int DEFAULT NULL,
q1 varchar(255) DEFAULT NULL,
q2 varchar(255) DEFAULT NULL,
q3 varchar(255) DEFAULT NULL,
PRIMARY KEY(RACEID, DRIVERID)
);
SQL
MERGE INTO QUALIFYING c
USING STGQUALIFYING n
ON
(n.RACEID = c.RACEID AND n.DRIVERID = c.DRIVERID)
WHEN MATCHED THEN
UPDATE SET
CONSTRUCTORID = n.CONSTRUCTORID, DRIVERNUMBER = n.DRIVERNUMBER, DRIVERPOSITION = n.DRIVERPOSITION, Q1 = n.Q1, Q2 = n.Q2, Q3 = n.Q3
WHEN NOT MATCHED THEN
INSERT (RACEID, DRIVERID, CONSTRUCTORID, DRIVERNUMBER, DRIVERPOSITION, Q1, Q2, Q3) VALUES
(RACEID, DRIVERID, CONSTRUCTORID, DRIVERNUMBER, DRIVERPOSITION, Q1, Q2, Q3);
The EXASolution user manual says:
The content of an identity column applies to the following rules:
If you specify an explicit value for the identity column while inserting a row, then this value is inserted.
In all other cases monotonically increasing numbers are generated by the system, but gaps can occur between the numbers.
and
You should not mistake an identity column with a constraint, i.e. identity columns do not guarantee unique values. But the values are unique as long as values are inserted only implicitly and are not changed manually.
You've put a primary key constraint on your identity column, so it must be unique. Since you are getting duplicates from your merge, either (a) you have, at some point, provided explicit values as in the first bullet above or updated a value manually, and the monotonically increasing sequence has reached a point where it is clashing with those existing values; or (b) there's a bug in their merge. The former seems more likely.
You can look at recently inserted value if you have one, or do a temporary insert of a new row (with merge) to see if it will create a row successfully, and if so whether you already have ID values higher than the one it allocates for that new row. If there are no higher values already, and insert works and merge continues to fail consistently, then it sounds like something you'd need to raise with EXASolution.

MySQL: Error 1628: Comment for table 'customer' is too long (max = 60)

After fixing Error 1253 (MySQL: Unable to fulling forward engineering Sakila (sample) into server), I have Error 1628.
Executing SQL script in server
ERROR: Error 1628: Comment for table 'customer' is too long (max = 60)
Scripts:
CREATE TABLE IF NOT EXISTS `sakila`.`customer` (
`customer_id` SMALLINT(5) UNSIGNED NOT NULL AUTO_INCREMENT ,
`store_id` TINYINT(3) UNSIGNED NOT NULL ,
`first_name` VARCHAR(45) NOT NULL ,
`last_name` VARCHAR(45) NOT NULL ,
`email` VARCHAR(50) NULL DEFAULT NULL ,
`address_id` SMALLINT(5) UNSIGNED NOT NULL ,
`active` TINYINT(1) NOT NULL DEFAULT TRUE ,
`create_date` DATETIME NOT NULL ,
`last_update` TIMESTAMP NULL DEFAULT CURRENT_TIMESTAMP ,
PRIMARY KEY (`customer_id`) ,
INDEX `idx_fk_store_id` (`store_id` ASC) ,
INDEX `idx_fk_address_id` (`address_id` ASC) ,
INDEX `idx_last_name` (`last_name` ASC) ,
CONSTRAINT `fk_customer_address`
FOREIGN KEY (`address_id` )
REFERENCES `sakila`.`address` (`address_id` )
ON DELETE RESTRICT
ON UPDATE CASCADE,
CONSTRAINT `fk_customer_store`
FOREIGN KEY (`store_id` )
REFERENCES `sakila`.`store` (`store_id` )
ON DELETE RESTRICT
ON UPDATE CASCADE)
ENGINE = InnoDB
DEFAULT CHARACTER SET = utf8
COMMENT 'Table storing all customers. Holds foreign keys to the address table and the store table where this customer is registered.\n\nBasic information about the customer like first and last name are stored in the table itself. Same for the date the record was created and when the information was last updated.'
SQL script execution finished: statements: 3 succeeded, 1 failed
As an addition: More current versions (5.6.X) allow longer comments. Unfortunately this length differs from the type of comment:
For tables: "A comment for the table, up to 2048 characters long."
For columns: "A comment for a column can be specified with the COMMENT option, up to 1024 characters long."
For INDEX: "In MySQL 5.6, index definitions can include an optional comment of up to 1024 characters."
For PARTITION: "Beginning with MySQL 5.6.6, the maximum length for a partition comment is 1024 characters. (Previously, this limit was not explicitly defined.)"
Source: http://dev.mysql.com/doc/refman/5.6/en/create-table.html
As stated in the MySQL docs, a comment is limited to 255 characters: http://dev.mysql.com/doc/refman/5.1/en/create-table.html#id3411882. Your comment is 305 characters, and it would seem, from the error message, that your particular MySQL install has a 60 character limit.

Table sync and copy into other table

I have two tables. Table A and Table B. They are identical. Ever 10 min i need to check if there any changs happend (New and updated) to Table A and copy into Table B. And also enter in Table C if i see a differance and new.
I also need to log if there any new records in Table A to table B and Table C
Iam planning to do join and compare the records. If i do that i might miss the new records. Is there any better way to do this kind of sync. It has to be done in SQL i can not use any other tools like SSIS.
Here's what I came up with in making some simple tables in SQL:
# create some sample tables and data
DROP TABLE alpha;
DROP TABLE beta;
DROP TABLE charlie;
CREATE TABLE `alpha` (
`id` INT(10) UNSIGNED NOT NULL AUTO_INCREMENT,
`data` VARCHAR(32) DEFAULT NULL,
PRIMARY KEY (`id`)
) ENGINE=MYISAM DEFAULT CHARSET=latin1;
CREATE TABLE `beta` (
`id` INT(10) UNSIGNED NOT NULL AUTO_INCREMENT,
`data` VARCHAR(32) DEFAULT NULL,
PRIMARY KEY (`id`)
) ENGINE=MYISAM DEFAULT CHARSET=latin1;
CREATE TABLE `charlie` (
`id` INT(10) UNSIGNED NOT NULL AUTO_INCREMENT,
`data` VARCHAR(32) DEFAULT NULL,
`type` VARCHAR(16) DEFAULT NULL,
PRIMARY KEY (`id`)
) ENGINE=MYISAM DEFAULT CHARSET=latin1;
INSERT INTO alpha (data) VALUES ("a"), ("b"), ("c"), ("d"), ("e");
INSERT INTO beta (data) VALUES ("a"), ("b"), ("c");
# note new records of A, log in C
INSERT INTO charlie (data, type)
(SELECT data, "NEW"
FROM alpha
WHERE id NOT IN
(SELECT id
FROM beta));
# insert new records of A into B
INSERT INTO beta (data)
(SELECT data
FROM alpha
WHERE id NOT IN
(SELECT id
FROM beta));
# make a change in alpha only
UPDATE alpha
SET data = "x"
WHERE data = "c";
# note changed records of A, log in C
INSERT INTO charlie (data, type)
(SELECT alpha.data, "CHANGE"
FROM alpha, beta
WHERE alpha.data != beta.data
AND alpha.id = beta.id);
# update changed records of A in B
UPDATE beta, alpha
SET beta.data = alpha.data
WHERE alpha.data != beta.data
AND alpha.id = beta.id;
You would of course have to expand this for the type of data, number of fields, etc. but this is a basic concept if it helps.
It's a pity that you can't use SSIS (not allowed?) because it's built for this kind of thing. Anyway, using pure SQL you should be able to something like the following: if your tables have got a created/updated timestamp column, then you could query Table B for the highest one and get all records from table A with timestamps higher than that one.
If there's no timestamp to use, hopefully there's a PK like an int that can be used in the same way.
Hope that helps?
Valentino.
I would try using a trigger or transactional replication.
Hopefully you have a good unique key that is used in the tables. To get new records you can do the following:
SELECT * FROM tableA
WHERE NOT EXISTS( SELECT * FROM tableB WHERE pkey.tableA = pkey.TableB)

updating DATE fields

I inherited MYSQL database that has lots of tables with data like
CREATE TABLE IF NOT EXISTS `ejl_registration` (
`id` int(11) NOT NULL auto_increment,
`team_id` int(11) default NULL,
`start_date` date default NULL,
`end_date` date default NULL,
PRIMARY KEY (`id`),
) ENGINE=InnoDB DEFAULT CHARSET=latin1 AUTO_INCREMENT=88668 ;
start_date and end_date should have values like:
2007-1-5, 2007-12-31
2008-1-1, 2008-12-31
2009-1-15,2009-12-31
But some of those en_date fields are either NULL or 0000-00-00.
Is there a ways to have single query to update all those invalid en_date fields and set their value to the end of the year equal to the year of start_date
Try this (please double check, I have not tested the command):
UPDATE `ejl_registration` SET `end_date`= CONCAT(YEAR(`start_date`),'-12-31')
WHERE `end_date` IS NULL OR `end_date` = '0000-00-00';
I don't know if DATEADD and DATEDIFF exist in MySQL, but I would strongly advise using some kind of date function rather than converting to strings and manipulating them that way.
In MS SQL SERVER this would work...
UPDATE
ejl_registration
SET
end_date = DATEADD(YEAR, 1 + DATEDIFF(YEAR, 0, start_date), 0)
WHERE
(end_date) IS NULL OR (end_date = '0000-00-00')