tags:

views:

132

answers:

2

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
            )
        )
    )
+3  A: 

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 Bannister
+1: Nice, complete, answer. I would like to add that dropping the [forever] column and changing the [endDate] default to "9999-12-31 23:59:59" would simplify the query even more.
Tony
A: 

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?

Dilip Shah
As Mark said, your original query was performing a cartesian join of [resource] to [resourceOrganization], which is when you join every row of one table to every row of another table. When there are no records in one of the tables you cannot perform this type of join. You would have to change the query to use a LEFT JOIN.
Tony
forget the work-a-round, until you learn and understand what was wrong, you'll continue to make the same mistakes
KM
Mark, Tony and KM, very much appreciate the correction in my approach
Dilip Shah