N年来我都回避了这个问题,最近不得不直面这个问题,于是在SQL Server 2012上作了调查,结论是和条件是否利用了表函数有关系。
顺便还证实了CASE WHEN语法里的条件绝对是短路评价,这在MSDN文档里明确有说明,这个东西可以用来提高速度。
SELECT * FROM test WHERE some_fast_check()=1 OR some_slow_check(c) = 1
SELECT * FROM test WHERE some_fast_check()=1 OR c IN (SELECT * FROM normal_or_temp_table)
SELECT * FROM test WHERE some_fast_check()=1 OR c IN (SELECT * FROM some_slow_data())
CASE WHEN 快速条件 THEN 1 WHEN 慢速条件 THEN 1 ELSE 0 END
SELECT * FROM test WHERE
CASE WHEN some_fast_check()=1 THEN 1
WHEN c IN (SELECT * FROM some_slow_data()) THEN 1
ELSE 0
END = 1
private Observable<String> slowPublisher = Observable.create(subscriber -> {
sleep(1); //sleep 1ms to just let other thread run so can get predictable output
println("[SLOW publisher] begin");
println("[SLOW publisher] do some work");
sleep(3000);
println("[SLOW publisher] publish");
subscriber.onNext("SLOW result");
subscriber.onCompleted();
println("[SLOW publisher] end");
}
);
@Test
public void test_publisher_subscriber_in_current_thread() throws Exception {
new Thread(() -> {
println("Enter test function");
slowPublisher
.subscribe(result -> {
println("---- subscriber got " + result);
});
println("Leave test function");
}, "CurrentThread" /*threadName*/).start();
assertOut("17:02:56.571 @CurrentThread Enter test function");
assertOut("17:02:56.667 @CurrentThread [SLOW publisher] begin");
assertOut("17:02:56.667 @CurrentThread [SLOW publisher] do some work");
assertOut("17:02:59.668 @CurrentThread [SLOW publisher] publish");
assertOut("17:02:59.668 @CurrentThread ---- subscriber got SLOW result");
assertOut("17:02:59.668 @CurrentThread [SLOW publisher] end");
assertOut("17:02:59.669 @CurrentThread Leave test function");
}
@Test
public void test_publisher_in_a_thread_and_subscriber_also_in_same() throws Exception {
new Thread(() -> {
println("Enter test function");
slowPublisher
.subscribeOn(schedulerForWorkThread1) //cause publisher run in new thread
.subscribe(result -> {
println("---- subscriber got " + result);
});
println("Leave test function");
}, "CurrentThread" /*threadName*/).start();
assertOut("11:49:51.169 @CurrentThread Enter test function");
assertOut("11:49:51.217 @CurrentThread Leave test function");
assertOut("11:49:51.218 @RxWorkThread1 [SLOW publisher] begin");
assertOut("11:49:51.218 @RxWorkThread1 [SLOW publisher] do some work");
assertOut("11:49:54.221 @RxWorkThread1 [SLOW publisher] publish");
assertOut("11:49:54.221 @RxWorkThread1 ---- subscriber got SLOW result");
assertOut("11:49:54.222 @RxWorkThread1 [SLOW publisher] end");
}
@Test
public void test_publisher_in_a_thread_and_subscriber_in_another() throws Exception {
new Thread(() -> {
println("Enter test function");
slowPublisher
.subscribeOn(schedulerForWorkThread1) //cause publisher run in new thread
.observeOn(schedulerForWorkThread2) //cause subscriber run in another new thread
.subscribe(result -> {
sleep(1); //sleep 1ms to just let other thread run so can get predictable output
println("---- subscriber got " + result);
});
println("Leave test function");
}, "CurrentThread" /*threadName*/).start();
assertOut("12:41:10.523 @CurrentThread Enter test function");
assertOut("12:41:10.676 @CurrentThread Leave test function");
assertOut("12:41:10.678 @RxWorkThread1 [SLOW publisher] begin");
assertOut("12:41:10.678 @RxWorkThread1 [SLOW publisher] do some work");
assertOut("12:41:13.682 @RxWorkThread1 [SLOW publisher] publish");
assertOut("12:41:13.682 @RxWorkThread1 [SLOW publisher] end");
assertOut("12:41:13.683 @RxWorkThread2 ---- subscriber got SLOW result");
}
private T blockForSingle(final Observable<? extends T> observable) {
final AtomicReference<T> returnItem = new AtomicReference<T>();
final AtomicReference<Throwable> returnException = new AtomicReference<Throwable>();
final CountDownLatch latch = new CountDownLatch(1);
Subscription subscription = ((Observable<T>)observable).subscribe(new Subscriber<T>() {
@Override
public void onCompleted() {
latch.countDown();
}
@Override
public void onError(final Throwable e) {
returnException.set(e);
latch.countDown();
}
@Override
public void onNext(final T item) {
returnItem.set(item);
}
});
BlockingUtils.awaitForComplete(latch, subscription);
if (returnException.get() != null) {
if (returnException.get() instanceof RuntimeException) {
throw (RuntimeException) returnException.get();
} else {
throw new RuntimeException(returnException.get());
}
}
return returnItem.get();
}
@Test
public void test_error_in_blocking_mode_will_always_throw_out_in_current_thread() throws Exception {
new Thread(() -> {
println("Enter test function");
try {
errorPublisher
.toBlocking().first();
} catch (Exception e) {
println("---- test1: " + e);
}
try {
errorPublisher
.subscribeOn(schedulerForWorkThread1) //cause publisher run in new thread
.toBlocking().first();
} catch (Exception e) {
println("---- test2: " + e);
}
try {
errorPublisher
.subscribeOn(schedulerForWorkThread1) //cause publisher run in new thread
.doOnError(e -> {/*do nothing*/})
.toBlocking().first();
} catch (Exception e) {
println("---- test3: " + e);
}
println("Leave test function");
}, "CurrentThread" /*threadName*/).start();
assertOut("14:38:43.078 @CurrentThread Enter test function");
assertOut("14:38:43.113 @CurrentThread ---- test1: java.lang.NullPointerException");
assertOut("14:38:43.126 @CurrentThread ---- test2: java.lang.NullPointerException");
assertOut("14:38:43.152 @CurrentThread ---- test3: java.lang.NullPointerException");
assertOut("14:38:43.152 @CurrentThread Leave test function");
}
@Test
public void test_error_subscribe_without_error_handler() throws Exception {
new Thread(() -> {
println("Enter test function");
try {
errorPublisher
.subscribeOn(schedulerForWorkThread1) //cause publisher run in new thread
.subscribe();
} catch (Exception e) {
println("---- should not come here : " + e);
}
println("Leave test function");
}, "CurrentThread" /*threadName*/).start();
assertOut("21:06:34.342 @CurrentThread Enter test function");
assertOut("21:06:34.397 @CurrentThread Leave test function");
}
@Test
public void test_error_subscribe_without_error_handler_cause_OnErrorNotImplementedException() throws Exception {
new Thread(() -> {
println("Enter test function");
try {
errorPublisher
.subscribe();
} catch (Exception e) {
println("---- test1 error: " + e);
}
try {
errorPublisher
.doOnError(e -> {/*do nothing*/})
.subscribe();
} catch (Exception e) {
println("---- test2 error: " + e);
}
println("Leave test function");
}, "CurrentThread" /*threadName*/).start();
assertOut("14:15:57.268 @CurrentThread Enter test function");
assertOut("14:15:57.304 @CurrentThread ---- test1 error: rx.exceptions.OnErrorNotImplementedException");
assertOut("14:15:57.308 @CurrentThread ---- test2 error: rx.exceptions.OnErrorNotImplementedException");
assertOut("14:15:57.308 @CurrentThread Leave test function");
}
@Test
public void test_error_subscribe_with_error_handler() throws Exception {
new Thread(() -> {
println("Enter test function");
errorPublisher
.subscribe(result -> {
//nothing
}, e -> { //onError
sleep(1); //sleep 1ms to just let other thread run so can get predictable output
println("-------- error1: " + e);
}, () -> { //onComplete
//nothing
});
errorPublisher
.subscribeOn(schedulerForWorkThread1) //cause publisher run in new thread
.subscribe(result -> {
//nothing
}, e -> { //onError
sleep(1); //sleep 1ms to just let other thread run so can get predictable output
println("-------- error2: " + e);
}, () -> { //onComplete
//nothing
});
println("Leave test function");
}, "CurrentThread" /*threadName*/).start();
assertOut("16:19:20.813 @CurrentThread Enter test function");
assertOut("16:19:20.830 @CurrentThread -------- error1: java.lang.NullPointerException");
assertOut("16:19:20.840 @CurrentThread Leave test function");
assertOut("16:19:20.843 @RxWorkThread1 -------- error2: java.lang.NullPointerException");
}
@Test
public void test_merge_in_parallel_but_result_order_not_predictable() throws Exception {
new Thread(() -> {
println("Enter test function");
slowPublisher
.subscribeOn(schedulerForWorkThread1) //cause publisher run in new thread
.mergeWith(
fastPublisher
.subscribeOn(schedulerForWorkThread2)
)
.subscribe(result -> {
println("---- subscriber got " + result);
});
println("Leave test function");
}, "CurrentThread" /*threadName*/).start();
assertOut("10:50:56.507 @CurrentThread Enter test function");
assertOut("10:50:56.637 @CurrentThread Leave test function");
assertOut("10:50:56.638 @RxWorkThread1 [SLOW publisher] begin");
assertOut("10:50:56.638 @RxWorkThread1 [SLOW publisher] do some work");
assertOut("10:50:56.690 @RxWorkThread2 [FAST publisher] begin");
assertOut("10:50:56.690 @RxWorkThread2 ---- subscriber got FAST result");
assertOut("10:50:56.691 @RxWorkThread2 [FAST publisher] end");
assertOut("10:50:59.640 @RxWorkThread1 [SLOW publisher] publish");
assertOut("10:50:59.640 @RxWorkThread1 ---- subscriber got SLOW result");
assertOut("10:50:59.641 @RxWorkThread1 [SLOW publisher] end");
}
@Test
public void test_merge_in_serial_if_not_specify_schedule_thread() throws Exception {
new Thread(() -> {
println("Enter test function");
slowPublisher
.mergeWith(
fastPublisher
)
.subscribe(result -> {
println("---- subscriber got " + result);
});
println("Leave test function");
}, "CurrentThread" /*threadName*/).start();
assertOut("10:52:59.765 @CurrentThread Enter test function");
assertOut("10:52:59.846 @CurrentThread [SLOW publisher] begin");
assertOut("10:52:59.846 @CurrentThread [SLOW publisher] do some work");
assertOut("10:53:02.849 @CurrentThread [SLOW publisher] publish");
assertOut("10:53:02.850 @CurrentThread ---- subscriber got SLOW result");
assertOut("10:53:02.850 @CurrentThread [SLOW publisher] end");
assertOut("10:53:02.904 @CurrentThread [FAST publisher] begin");
assertOut("10:53:02.904 @CurrentThread ---- subscriber got FAST result");
assertOut("10:53:02.904 @CurrentThread [FAST publisher] end");
assertOut("10:53:02.905 @CurrentThread Leave test function");
}
@Test
public void test_concat_will_always_serially_so_predictable_order() throws Exception {
new Thread(() -> {
println("Enter test function");
slowPublisher
.subscribeOn(schedulerForWorkThread1) //cause publisher run in new thread
.concatWith(
fastPublisher
.subscribeOn(schedulerForWorkThread2)
)
.subscribe(result -> {
println("---- subscriber got " + result);
});
println("Leave test function");
}, "CurrentThread" /*threadName*/).start();
assertOut("10:53:55.314 @CurrentThread Enter test function");
assertOut("10:53:55.454 @CurrentThread Leave test function");
assertOut("10:53:55.456 @RxWorkThread1 [SLOW publisher] begin");
assertOut("10:53:55.456 @RxWorkThread1 [SLOW publisher] do some work");
assertOut("10:53:58.461 @RxWorkThread1 [SLOW publisher] publish");
assertOut("10:53:58.461 @RxWorkThread1 ---- subscriber got SLOW result");
assertOut("10:53:58.463 @RxWorkThread1 [SLOW publisher] end");
assertOut("10:53:58.516 @RxWorkThread2 [FAST publisher] begin");
assertOut("10:53:58.516 @RxWorkThread2 ---- subscriber got FAST result");
assertOut("10:53:58.517 @RxWorkThread2 [FAST publisher] end");
}
private static class RxResult<T> {
T result;
Throwable error;
}
private static class RxResultAndSignal<T> extends RxResult<T> {
CountDownLatch latch = new CountDownLatch(1);
}
@Test
public void test_merge_3_different_type_rx_by_self_idea() throws Exception {
new Thread(() -> {
println("Enter test function");
RxResultAndSignal<String> r1 = new RxResultAndSignal<>();
RxResultAndSignal<StringBuilder> r2 = new RxResultAndSignal<>();
RxResultAndSignal<Number> r3 = new RxResultAndSignal<>();
slowPublisher
.subscribeOn(schedulerForWorkThread1) //cause publisher run in new thread
.subscribe(result -> {
r1.result = result;
}, e -> { //onError
r1.error = e;
r1.latch.countDown();
}, () -> { //onComplete
r1.latch.countDown();
});
incompatiblePublisher
.subscribeOn(schedulerForWorkThread2)
.subscribe(result -> {
r2.result = result;
}, e -> { //onError
r2.error = e;
r2.latch.countDown();
}, () -> { //onComplete
r2.latch.countDown();
});
buggyPublisher
.subscribeOn(schedulerForWorkThread3)
.subscribe(result -> {
r3.result = result;
}, e -> { //onError
r3.error = e;
r3.latch.countDown();
}, () -> { //onComplete
r3.latch.countDown();
});
try {
r1.latch.await();
r2.latch.await();
r3.latch.await();
} catch (InterruptedException e) {
e.printStackTrace();
}
sleep(1); //sleep 1ms to let other thread run so can get predictable output
println("---- got all result: {" + r1.result + "}, {" + r2.result + "}, {" + r3.result + "}");
println("---- got all error: {" + r1.error + "}, {" + r2.error + "}, {" + r3.error + "}");
println("Leave test function");
}, "CurrentThread" /*threadName*/).start();
assertOut("13:27:55.033 @CurrentThread Enter test function");
assertOut("13:27:55.065 @RxWorkThread1 [SLOW publisher] begin");
assertOut("13:27:55.065 @RxWorkThread1 [SLOW publisher] do some work");
assertOut("13:27:55.082 @RxWorkThread2 [Incompatible publisher] begin");
assertOut("13:27:55.082 @RxWorkThread2 [Incompatible publisher] do some work");
assertOut("13:27:55.086 @RxWorkThread3 [Buggy publisher] begin");
assertOut("13:27:57.084 @RxWorkThread2 [Incompatible publisher] end");
assertOut("13:27:58.069 @RxWorkThread1 [SLOW publisher] publish");
assertOut("13:27:58.070 @RxWorkThread1 [SLOW publisher] end");
assertOut("13:27:58.071 @CurrentThread ---- got all result: {SLOW result}, {Incompatible result}, {null}");
assertOut("13:27:58.071 @CurrentThread ---- got all error: {null}, {null}, {java.lang.NullPointerException}");
assertOut("13:27:58.071 @CurrentThread Leave test function");
}
@Test
public void test_merge_3_different_type_rx() throws Exception {
new Thread(() -> {
println("Enter test function");
RxResult<String> r1 = new RxResult<>();
RxResult<StringBuilder> r2 = new RxResult<>();
RxResult<Number> r3 = new RxResult<>();
Completable.merge(
slowPublisher
.subscribeOn(schedulerForWorkThread1) //cause publisher run in new thread
.doOnNext(result -> r1.result = result)
.doOnError(e -> r1.error = e)
.toCompletable() //so can mergeWith other type rx
.onErrorComplete() //auto call complete when error
,
incompatiblePublisher
.subscribeOn(schedulerForWorkThread2)
.doOnNext(result -> r2.result = result)
.doOnError(e -> r2.error = e)
.toCompletable() //so can mergeWith other type rx
.onErrorComplete() //auto call complete when error
,
buggyPublisher
.subscribeOn(schedulerForWorkThread3)
.doOnNext(result -> r3.result = result)
.doOnError(e -> r3.error = e)
.toCompletable() //so can mergeWith other type rx
.onErrorComplete() //auto call complete when error
)
.await(/*can specify total timeout*/);
sleep(1); //sleep 1ms to let other thread run so can get predictable output
println("---- got all result: {" + r1.result + "}, {" + r2.result + "}, {" + r3.result + "}");
println("---- got all error: {" + r1.error + "}, {" + r2.error + "}, {" + r3.error + "}");
println("Leave test function");
}, "CurrentThread" /*threadName*/).start();
assertOut("13:47:50.789 @CurrentThread Enter test function");
assertOut("13:47:50.852 @RxWorkThread1 [SLOW publisher] begin");
assertOut("13:47:50.852 @RxWorkThread1 [SLOW publisher] do some work");
assertOut("13:47:50.862 @RxWorkThread2 [Incompatible publisher] begin");
assertOut("13:47:50.863 @RxWorkThread2 [Incompatible publisher] do some work");
assertOut("13:47:50.869 @RxWorkThread3 [Buggy publisher] begin");
assertOut("13:47:52.868 @RxWorkThread2 [Incompatible publisher] end");
assertOut("13:47:53.854 @RxWorkThread1 [SLOW publisher] publish");
assertOut("13:47:53.854 @RxWorkThread1 [SLOW publisher] end");
assertOut("13:47:53.856 @CurrentThread ---- got all result: {SLOW result}, {Incompatible result}, {null}");
assertOut("13:47:53.856 @CurrentThread ---- got all error: {null}, {null}, {java.lang.NullPointerException}");
assertOut("13:47:53.856 @CurrentThread Leave test function");
}
docker run -it -v C:¥WinTestDir:/xxx/yyy busybox
结果会提示说"和:非法字符。
docker run -it -v /WinTestDir:/xxx/yyy busybox
docker run -it -v /c/WinTestDir:/xxx/yyy busybox
结果容器里的/xxx/yyy目录下是空的。
docker-machine ssh default
docker@rethink:~$ ls /c
Users
docker@rethink:~$ ls /c/Users/Administrator
... Documents Downloads ...
docker@rethink:~$ ls /WinTestDir
No such file or directory
docker run -it -v /c/Users/q/Documents/WinTestDir:/xxx/yyy busybox
/ # ls /xxx/yyy
... some files ...
test:
image: busybox
command: /bin/find /xxx/yyy
volumes:
- ./someHostDir:/xxx/yyy
$ docker-compose config
networks: {}
services:
test:
command: /bin/find /xxx/yyy
image: busybox
network_mode: bridge
volumes:
- /private/tmp/docker-compose-test/someHostDir:/xxx/yyy:rw
version: '2.0'
volumes: {}
$ docker-compose up
Recreating dockercomposetest_test_1
Attaching to dockercomposetest_test_1
test_1 | /xxx/yyy
dockercomposetest_test_1 exited with code 0